当前位置: 首页 > 知识库问答 >
问题:

Java(Graphics2D):由创建的Graphics2D绘制的三角形直到第二次重新绘制才可见

史懿轩
2023-03-14

我有以下最小的代码来用箭头头画一条线:

package gui;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;

import javax.swing.JPanel;

public class StateBtn extends JPanel {

    private static final long serialVersionUID = -431114028667352251L;

    @Override
    protected void paintComponent(Graphics g) {

        super.paintComponent(g);

        // enable antialiasing
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);


        // draw the arrow
        Line2D.Double line = new Line2D.Double(0, getHeight()/2, 20, getHeight()/2);            
        drawArrowHead(g2, line);
        g2.draw(line);

        // If I call repaint() here (like in my answer below), it works

    }

    private void drawArrowHead(Graphics2D g2d, Line2D.Double line) {  
        AffineTransform tx = new AffineTransform();

        tx.setToIdentity();
        double angle = Math.atan2(line.y2-line.y1, line.x2-line.x1);
        tx.translate(line.x2, line.y2);
        tx.rotate((angle-Math.PI/2d));  

        Polygon arrowHead = new Polygon();  
        arrowHead.addPoint(0,5);
        arrowHead.addPoint(-5,-5);
        arrowHead.addPoint(5,-5);

        Graphics2D g = (Graphics2D) g2d.create();
        g.setTransform(tx);   
        g.fill(arrowHead);
        g.dispose();
    }

}

它是这样创建的:

package gui;

import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class Main extends JFrame {

    private static final long serialVersionUID = 4085389089535850911L;
    private JPanel contentPane;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Main frame = new Main();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public Main() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setSize(500, 500);
        setLocation(0, 0);

        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        StateBtn stateBtn = new StateBtn();
        stateBtn.setBounds(200,200,35,35);
        contentPane.add(stateBtn);
    }
}

线画得很好,但箭头头是不可见的,直到我调用repaint()。问题是,该元素是一个可拖动的元素,因此每次更改位置时,我都必须调用repaint()两次。这会使代码更加复杂,并且GUI会很落后。

为什么箭头不能直接和线画在一起呢?真的没有人能帮我吗?

共有1个答案

简滨海
2023-03-14

您没有发布真正的MCVE,因此不可能知道您可能做错了什么,但不需要在回答中使用的kludge,即在PaintComponent中重新调用repaint()。如果您仍然需要帮助与您自己的代码,那么请张贴一个有效的MCVE,代码我们可以编译和运行不需要修改。关于我所说的MCVE的例子,请阅读MCVE链接并查看我在下面的答案中发布的示例MCVE。

说到这里,请理解Swing图形通常是被动的,这意味着您将让程序根据事件更改其状态,然后调用repaint(),这将建议Swing重新绘制管理器调用paint。不能保证绘画会发生,因为已经“堆叠”的重绘请求,由于在短时间内调用了许多重绘请求而正在备份的重绘请求可能会被忽略。

所以在您的情况下,我们可以使用您的代码并对其进行修改,看看这是如何工作的。假设我为JPanel提供了一个MouseAdapter--一个同时是MouseListener和MouseMotionListener的类,在这个适配器中,我简单地设置了两个点实例字段,P0--用于鼠标最初按下的位置,P1--用于鼠标拖动或释放的位置。我可以设置这些字段然后调用repaint,让我的绘画方法使用p0和p1来绘制我的箭头。因此鼠标适配器可能如下所示:

private class MyMouse extends MouseAdapter {
    private boolean settingMouse = false;

    @Override
    public void mousePressed(MouseEvent e) {
        if (e.getButton() != MouseEvent.BUTTON1) {
            return;
        }
        p0 = e.getPoint();
        p1 = null;
        settingMouse = true; // drawing a new arrow
        repaint();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        setP1(e);
        settingMouse = false; // no longer drawing the new arrow
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        setP1(e);
    }

    private void setP1(MouseEvent e) {
        if (settingMouse) {
            p1 = e.getPoint();
            repaint();
        }
    }
}

然后在我的绘画代码中,我会使用你的代码,修改后使它使用我的p0和p1点:

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

    if (p0 != null && p1 != null) {
        Line2D.Double line = new Line2D.Double(p0.x, p0.y, p1.x, p1.y);

        drawArrowHead(g2, line);
        g2.draw(line);          
    }

}

整个事情看起来是这样的:

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.*;
import javax.swing.*;

@SuppressWarnings("serial")
public class StateBtn extends JPanel {
    // constants to size the JPanel
    private static final int PREF_W = 800;
    private static final int PREF_H = 650;
    private static final int AH_SIZE = 5; // size of arrow head -- avoid "magic"
                                            // numbers!

    // our start and end Points for the arrow
    private Point p0 = null;
    private Point p1 = null;

    public StateBtn() {
        // create and add a label to tell the user what to do
        JLabel label = new JLabel("Click Mouse and Drag");
        label.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 42));
        label.setForeground(new Color(0, 0, 0, 50));
        setLayout(new GridBagLayout());
        add(label); // add it to the center

        // create our MouseAdapater and use it as both MouseListener and
        // MouseMotionListener
        MyMouse myMouse = new MyMouse();
        addMouseListener(myMouse);
        addMouseMotionListener(myMouse);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        // only do this if there are points to draw!
        if (p0 != null && p1 != null) {
            Line2D.Double line = new Line2D.Double(p0.x, p0.y, p1.x, p1.y);

            drawArrowHead(g2, line);
            g2.draw(line);
        }

    }

    private void drawArrowHead(Graphics2D g2d, Line2D.Double line) {
        AffineTransform tx = new AffineTransform();

        tx.setToIdentity();
        double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
        tx.translate(line.x2, line.y2);
        tx.rotate((angle - Math.PI / 2d));

        Polygon arrowHead = new Polygon();
        arrowHead.addPoint(0, AH_SIZE); // again avoid "magic" numbers
        arrowHead.addPoint(-AH_SIZE, -AH_SIZE);
        arrowHead.addPoint(AH_SIZE, -AH_SIZE);

        Graphics2D g = (Graphics2D) g2d.create();
        g.setTransform(tx);
        g.fill(arrowHead);
        g.dispose(); // we created this, so we can dispose of it
        // we should **NOT** dispose of g2d since the JVM gave us that
    }

    @Override
    public Dimension getPreferredSize() {
        // size our JPanel
        return new Dimension(PREF_W, PREF_H);
    }

    private class MyMouse extends MouseAdapter {
        private boolean settingMouse = false;

        @Override
        public void mousePressed(MouseEvent e) {
            // if we press the wrong mouse button, exit
            if (e.getButton() != MouseEvent.BUTTON1) {
                return;
            }
            p0 = e.getPoint();  // set the start point
            p1 = null;  // clear the end point
            settingMouse = true; // tell mouse listener we're creating a new arrow
            repaint();  // suggest a repaint
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            setP1(e);
            settingMouse = false; // no longer drawing the new arrow
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            setP1(e);
        }

        private void setP1(MouseEvent e) {
            if (settingMouse) {
                p1 = e.getPoint(); // set the end point
                repaint();  // and paint!
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui() {
        StateBtn mainPanel = new StateBtn();
        JFrame frame = new JFrame("StateBtn");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

这段代码就是我的MCVE示例的意思。实际上,对于一个像样的MCVE来说,它有点大,但它也可以。请编译并运行代码以确保其工作。如果这对你没有帮助,如果你仍然必须使用一个与你的重新绘制调用,那么我敦促你创建你自己的MCVE和张贴它与你的问题,然后评论给我,以便我可以看到它。

顺便说一句,有人问像在drawarrowhead(...)方法中那样创建一个新的图形对象是否可以,是的,这不仅可以,而且是处理仿射转换时最好做的事情,因为这样您就不必担心转换可能对可能共享原始图形对象的边框和子组件产生的下游影响。这也是可以的,只要您遵循这样的规则,即处理您自己创建的图形对象,而不处理JVM提供给您的图形对象。

 类似资料:
  • 问题内容: 我正在尝试使用Java的Graphics2D在屏幕上绘制图像。这是我正在使用的代码。我想看到图像在屏幕上稳定移动。目前,我可以看到图像,但是除非调整窗口大小,否则图像不会移动,在这种情况下,图像确实会移动。我已经勾勒出以下课程。 传递给Tester的Component对象是以下类: 我确保此类仅添加了一个精灵。Sprite类大致如下: 但是,我在屏幕上仅看到固定的Bowser图像。除非

  • 问题内容: 我想在另一幅图像上绘制部分透明的图像(在物体上形成阴影)。我目前正在使用Java的Graphics2D类进行渲染,有人告诉我将合成设置为AlphaComposite,但这仅将其设置为完全透明。 我可以使用当前设置进行此操作吗?我必须怎么做才能解决此问题? 这是我被告知可以使其部分透明的代码: (顺便说一句,我正在使用png图像) 这是您的sscce(它们都在不同的类中,但是为了简单起见

  • 我试图保存一个自助式图像,我已经画了一个新的文件。我打开这个文件,使用图形2d在上面绘制(然后在JFrame中显示图像,以确保其工作,它确实这样做),然后将其保存到一个新文件中。 问题是:保存的文件只是原始图像。它不包含我在上面绘制的任何新图形。 以下是我的代码的简化版本: 稍后在另一种方法中: 相关方法:

  • 我试图用DXUT11画一个简单的三角形,但是我在窗口中看不到三角形。 我努力工作,但仍然找不到问题所在-_- 我检查了D3D函数的每个返回值,它们都返回了S_OK<我可以看到我的窗口显示出来,背景色是我设置为清除窗口的颜色,因此看起来d3d设备是当前创建的,并且后缓冲区被渲染到窗口,但是黄色三角形在哪里 T\u T。。。。。。。。。。。。。。。 我的代码如下:(为了简化问题,我已经删除了所有的检查

  • 本文向大家介绍用CSS绘制一个三角形相关面试题,主要包含被问及用CSS绘制一个三角形时的应答技巧和注意事项,需要的朋友参考一下