简体   繁体   English

Java Swing难题:JPanel上不显示第一次迭代?

[英]A Java Swing puzzle: What doesn't the first iteration display on the JPanel?

Here's one I've come close to figuring out, but never really did. 这是我接近解决的问题,但从未真正做到。

The code listed below is supposed to draw a green circle as soon as it sees its first click. 下面列出的代码在第一次点击时便会绘制一个绿色圆圈。 It doesn't. 没有。 Subsequent clicks draw lines that connect the current clicked point with the previous one, in red. 后续点击会以红色绘制连接当前点击点和上一个点击点的线。 The code fails for the first click and works on all subsequent ones. 该代码在首次点击时失败,并且在所有后续点击中均有效。 Why doesn't the first click display? 为什么第一次点击不显示? It runs! 它运行!

What am I doing wrong? 我究竟做错了什么?

The code should compile on any current JDE. 该代码应在任何当前的JDE上编译。

TIA TIA

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;

class Demo extends JFrame
            implements  ActionListener, ListSelectionListener, MouseListener {

  int clkCt = 0, // Count of the number of clicks we've done.
      oldX,      // Penultimate X value
      oldY,      // Penultimate X value
      scrH,      // Height of the drawing canvas.
      scrW;      // Width of the drawing canvas.

  JFrame f;      // Holder for the drawing canvas.

  JLabel ctL;    // Displays the number of clicks we've done.

  JPanel canvas; // The drawing canvas.

  public void demoLines() {

    Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
    scrH = (int) ((double) d.height * 0.75);
    scrW = (int) ((double) d.width * 0.75);

    oldX = scrH / 2;
    oldY = oldX;

    // Create and set up the window.
    f = new JFrame("Multi Click Demo");
    f.getContentPane().setLayout(null);
    int h = scrH / 5;
    f.setBounds(h, h, scrW, scrH);

    // Create a panel
    canvas = new JPanel();
    canvas.setBackground(Color.black);
    canvas.setForeground(Color.red);
    canvas.setLayout(null);
    canvas.setBounds(0, 0, scrW, scrH);
    canvas.setPreferredSize(new Dimension(scrW, scrH));
    canvas.addMouseListener(this);
    f.getContentPane().add(canvas);

    // Create the exit button.
    JButton exit = new JButton("Exit");
    exit.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        goAway();
      }
    });
    exit.setBackground(Color.black);
    exit.setForeground(Color.red);
    exit.setBounds(0, 0, (scrW / 15), (scrH / 15));
    canvas.add(exit); //*/

    // Create the label for the click count.
    ctL = new JLabel("None Yet");
    ctL.setBackground(Color.black);
    ctL.setForeground(Color.red);
    ctL.setBounds((scrH / 15), (scrH * 13 / 15), (scrW / 15), (scrH / 15));
    canvas.add(ctL);

    f.getContentPane().add(canvas);
    f.setVisible(true);

    Graphics g = canvas.getGraphics();
    if (g == null) {
      System.out.println("No graphics for canvas!");
    } else {
      canvas.revalidate(); // This didn't help.
      paintComponent(g, (oldX + oldX / 2), (oldY + oldY / 2));
    }
  }

  void goAway() {
    f.setVisible(false);
    f.dispose();
  }

  public void mouseClicked(MouseEvent m) {
    // Where was the mouse clicked?
    int clkdBtn = m.getButton(),
        x = m.getX(),
        y = m.getY();
    Graphics g = canvas.getGraphics();
    paintComponent(g, x, y);
  }

  public void paintComponent(Graphics g,
                             int x,
                             int y) {

    // This always runs.
    ctL.setText(clkCt + "");

    if (clkCt == 0) {
      // This never displays!
      g.setColor(Color.green);
      int r = scrH * 4 / 5;
      g.drawOval((scrH / 10), (scrH / 10), r, r);
    }

   g.setColor(Color.red);
   g.drawLine(oldX, oldY, x, y);
   oldX = x;
    oldY = y;
    clkCt++;
  }


  public void actionPerformed(ActionEvent event) { }
  public void valueChanged(ListSelectionEvent event) { }

  public void mouseEntered(MouseEvent e) { }
  public void mouseExited(MouseEvent e) { }
  public void mousePressed(MouseEvent e) { }
  public void mouseReleased(MouseEvent e) { }

  public static void main(String[] s) {

    Demo m = new Demo();
    m.demoLines();
  }
}

What am I doing wrong? 我究竟做错了什么?

You're using getGraphics for custom painting. 您正在使用getGraphics进行自定义绘画。 Any previous painting will be lost when a repaint is called. 调用repaint时,以前的任何画都将丢失。 Instead move all the custom painting functionality to a new class based on JComponent or JPanel and override paintComponent there. 而是将所有自定义绘画功能移至基于JComponentJPanel的新类,并在那里覆盖paintComponent Remember to invoke super.paintComponent(g) . 记住要调用super.paintComponent(g)

See Custom Painting Approaches for solutions for drawing multiple components from within paintComponent . 有关从paintComponent内绘制多个组件的解决方案,请参见“ 自定义绘画方法 ”。 You can build a List<Shape> of custom drawable components, iterate through the list from within the method, and use drawLine or drawOval as appropriate. 您可以构建自定义可绘制组件的List<Shape> ,从方法中遍历列表,并根据需要使用drawLinedrawOval

remove the following lines from demolines() demolines()删除以下行

Graphics g = canvas.getGraphics();
if (g == null) {
  System.out.println("No graphics for canvas!");
} else {
  canvas.revalidate(); // This didn't help.
  paintComponent(g, (oldX + oldX / 2), (oldY + oldY / 2));
}

and add canvas.addMouseListener(this); 并添加canvas.addMouseListener(this); in the end of the function 在功能的最后

There were a multitude of problems with that code. 该代码存在许多问题。 I fixed them but neglected to document every change. 我固定了它们,但忽略了所有更改。 Look closely over this code and ask questions if you do not understand (from reading the relevant documentation/tutorial) why I made the change. 仔细查看此代码,并询问您是否不理解(通过阅读相关文档/教程)为什么进行更改。

在此处输入图片说明

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.EmptyBorder;

public class Demo extends JPanel
        implements ListSelectionListener, MouseListener {

    int clkCt = 0, // Count of the number of clicks we've done.
            oldX, // Penultimate X value
            oldY, // Penultimate X value
            scrH = 100, // Height of the drawing canvas.
            scrW = 400;      // Width of the drawing canvas.
    JLabel ctL;    // Displays the number of clicks we've done.
    int x, y;

    public void demoLines() {
        oldX = scrH / 2;
        oldY = oldX;

        // Create a panel
        setBackground(Color.black);
        setForeground(Color.red);
        setPreferredSize(new Dimension(scrW, scrH));

        // Create the label for the click count.
        ctL = new JLabel("None Yet");
        ctL.setBackground(Color.black);
        ctL.setForeground(Color.red);
        ctL.setBounds((scrH / 15), (scrH * 13 / 15), (scrW / 15), (scrH / 15));
        add(ctL);
        addMouseListener(this);
    }

    public void mouseClicked(MouseEvent m) {
        // Where was the mouse clicked?
        x = m.getX();
        y = m.getY();
        repaint();
    }

    @Override
    public void mousePressed(MouseEvent e) {}

    @Override
    public void mouseReleased(MouseEvent e) {}

    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) {}

    public void paintComponent(Graphics g) {

        // This always runs.
        ctL.setText(clkCt + "");

        if (clkCt == 0) {
            // This never displays!
            g.setColor(Color.green);
            int r = scrH * 4 / 5;
            g.drawOval((scrH / 10), (scrH / 10), r, r);
        }

        g.setColor(Color.red);
        g.drawLine(oldX, oldY, x, y);
        oldX = x;
        oldY = y;
        clkCt++;
    }

    public void valueChanged(ListSelectionEvent event) {
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                Demo m = new Demo();
                m.demoLines();
                JFrame f = new JFrame("Demo");
                f.add(m);
                // Ensures JVM closes after frame(s) closed and
                // all non-daemon threads are finished
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                // See http://stackoverflow.com/a/7143398/418556 for demo.
                f.setLocationByPlatform(true);

                // ensures the frame is the minimum size it needs to be
                // in order display the components within it
                f.pack();
                // should be done last, to avoid flickering, moving,
                // resizing artifacts.
                f.setVisible(true);
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    }
}

Notes 笔记

  1. Don't extend frame or other top level containers. 不要扩展框架或其他顶层容器。 Instead create & use an instance of one. 而是创建并使用一个实例。
  2. Java GUIs might have to work on a number of platforms, on different screen resolutions & using different PLAFs. Java GUI可能必须在多种平台上,不同的屏幕分辨率和使用不同的PLAF上运行。 As such they are not conducive to exact placement of components. 因此,它们不利于组件的精确放置。 To organize the components for a robust GUI, instead use layout managers, or combinations of them, along with layout padding & borders for white space. 要为健壮的GUI组织组件,请使用布局管理器或它们的组合,以及空白的布局填充和边框。
  3. Instead of painting in a top level container such as JFrame , add a JPanel & do custom painting in the paintComponent(Graphics) method. 代替在顶级容器(例如JFrame中绘画,可以添加JPanel并在paintComponent(Graphics)方法中进行自定义绘画。 Also return a sensible preferred size for the custom component, to assist the layout manager. 还为定制组件返回合理的首选大小,以协助布局管理器。
  4. For custom painting in a JComponent , override paintComponent(Graphics) instead of paint(Graphics) . 对于JComponent自定义绘画,请重写paintComponent(Graphics)而不是paint(Graphics)

覆盖paintComponent()时,应始终将super.paintComponent()作为第一行。

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

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