java - 自定义绘画和 Z 顺序 : query re painting order

标签 java swing paintcomponent z-order custom-painting

所以,我想对 JFrame 中的一些组件进行 Z 排序。

组件:

public class aBLUEBox extends JPanel{
    int xPos = 19;
    int yPos = 20;
    int width = 10;
    int height = 80;
    
    public void paintBox(Graphics g){
         g.setColor(Color.BLUE);
         g.fillRect(xPos,yPos,width,height);
    }
}

框架:

public class CreateWindow extends JFrame{
    CreateWindow(){
        this.setTitle("Layering Test");
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        
        this.setSize(1920/2,1080/2);
        this.setLocationRelativeTo(null);
        this.setResizable(false);
        
        this.setVisible(true);
    }
}

将组件添加到框架/主类中:

public class LayerMain {
    CreateWindow window;

    static aBLUEBox BLUEBox;
    static aREDBox REDBox; //A Different Component just like aBLUEBox, but with an altered PaintBox() method which paints a red box instead of a blue one.

    PanelRenderer RendererP;
    
    LayerMain(){
        BLUEBox = new aBLUEBox();
        REDBox = new aREDBox();
        
        RendererP = new PanelRenderer();  //holds the PaintComponent Method. Class for this is shown below.
        
        window = new CreateWindow();
        window.add(BLUEBox);
        window.add(REDBox);

        window.setComponentZOrder(BLUEBox, 0);
        window.setComponentZOrder(REDBox, 0); //puts red on 0, moving blue up to 1.
//So now, BLUEBox's Z-order is 1, thus BLUEBox is on top of REDBox.

        System.out.println("Z-order of blue = " + window.getComponentZOrder(BLUEBox)); //Prints 1
        System.out.println("Z-order of Red = " + window.getComponentZOrder(REDBox)); //Prints 0

        window.add(RendererP);

        RendererP.repaint();  //Should Paint both box's.
     }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() { 
                new LayerMain();
            }
        });
    }
}

然后,我想使用 repaint() 调用渲染这些组件。

渲染器:

public class PanelRenderer extends JPanel{

    public void paintComponent(Graphics g){ 
        super.paintComponent(g); 
        //JPanels:
        LayerMain.BLUEBox.paintBox(g);  //Paints Blue first, not that it should matter.
        LayerMain.REDBox.paintBox(g);   //Paints Red Second, not that it should matter.

        System.out.println("PaintComponent invoked.");
    }
}

顶部/底部渲染的内容应与框架中组件的 Z 索引相对应。 (例如,索引 1 处的组件应在索引 0 的组件之上渲染)

但是,当渲染器 (JPanel) 添加到窗口 (JFrame) 并调用 paintComponent 时,不会发生任何情况。从字面上看,没有任何绘画。

注释掉主类中的 Z 顺序代码使得至少有一些东西实际上可以绘制,但是红色绘制在蓝色之上(因为在 PaintComponent 方法中红色是最后绘制的,因此在顶部),这不是我想要的。

//window.setComponentZOrder(BLUEBox, 0);
//window.setComponentZOrder(REDBox, 0); //puts red on 0, moving blue up to 1.

为什么组件按照它们在 paintComponent 中调用的顺序显示,而不是按照它们在 JFrame 中设置的顺序显示?

MRE/SSCCE

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

public class LayerMain {
    CreateWindow window;

    static ColoredBox blueBox;
    //A Different Component just like aBLUEBox, but with an altered 
    // PaintBox() method which paints a red box instead of a blue one.
    static ColoredBox redBox; 

    PanelRenderer rendererP;

    LayerMain(){
        blueBox = new ColoredBox(Color.BLUE);
        redBox = new ColoredBox(Color.RED);

        //holds the PaintComponent Method. Class for this is shown below.
        rendererP = new PanelRenderer();  

        window = new CreateWindow();
        window.add(blueBox);
        window.add(redBox);

        window.setComponentZOrder(blueBox, 0);
        window.setComponentZOrder(redBox, 0); //puts red on 0, moving blue up to 1.
        //So now, blueBox's Z-order is 1, thus blueBox is on top of redBox.

        System.out.println("Z-order of blue = " + window.getComponentZOrder(blueBox)); //Prints 1
        System.out.println("Z-order of Red = " + window.getComponentZOrder(redBox)); //Prints 0

        window.add(rendererP);

        rendererP.repaint();  //Should Paint both box's.
     }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new LayerMain();
        });
    }
}

class PanelRenderer extends JPanel{

    @Override
    public void paintComponent(Graphics g){ 
        super.paintComponent(g); 
        //JPanels:
        LayerMain.blueBox.paintBox(g);  //Paints Blue first, not that it should matter.
        LayerMain.redBox.paintBox(g);   //Paints Red Second, not that it should matter.

        System.out.println("PaintComponent invoked.");
    }
}

class CreateWindow extends JFrame{
    CreateWindow(){
        this.setTitle("Layering Test");
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);

        this.setSize(1920/2,1080/2);
        this.setLocationRelativeTo(null);
        this.setResizable(false);

        this.setVisible(true);
    }
}

class ColoredBox extends JPanel {
    int xPos = 19;
    int yPos = 20;
    int width = 10;
    int height = 80;
    Color color;
    
    ColoredBox(Color color) {
        super();
        this.color = color;
    }

    public void paintBox(Graphics g){
         g.setColor(color);
         g.fillRect(xPos,yPos,width,height);
    }
}

最佳答案

好吧,不。这绝对不是您应该尝试执行自定义绘画或使用自定义组件的方式。您不负责组件的渲染,因此您的 PanelRenderer 没有任何意义,您也不应该真正使用这种方式的 static ,它只是懒惰且容易出错。

要么使其 100% 自定义绘制,要么 100% 面向组件,而不是两者兼而有之

Why do the components display in the order that they are called within the PaintComponent, and not displayed in the order that they are set to within the JFrame?

因为你不负责绘制组件并且你正在搞乱系统

组件/ZOrder 示例

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JPanel blueBox;
        private JPanel redBox;

        private JPanel top, bottom;

        public TestPane() {
            blueBox = makeBox(Color.BLUE);
            redBox = makeBox(Color.RED);

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            // Filler
            add(emptyBox(), gbc);

            gbc.gridwidth = 2;
            gbc.gridheight = 2;
            gbc.fill = GridBagConstraints.BOTH;
            add(blueBox, gbc);

            gbc.gridx++;
            gbc.gridy++;
            add(redBox, gbc);

            // Filler
            gbc.gridx++;
            gbc.gridy++;
            gbc.gridwidth = 1;
            gbc.gridheight = 1;
            gbc.fill = GridBagConstraints.NONE;
            add(emptyBox(), gbc);

            JButton flip = new JButton("Flip");
            flip.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    setComponentZOrder(bottom, 0);
                    JPanel temp = top;
                    top = bottom;
                    bottom = temp;
                    repaint();
                }
            });

            gbc.gridx = 0;
            gbc.gridy = 4;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            add(flip, gbc);

            top = blueBox;
            bottom = redBox;
        }

        protected JPanel emptyBox() {
            JPanel box = new JPanel() {
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(50, 50);
                }
            };
            box.setOpaque(false);
            return box;
        }

        protected JPanel makeBox(Color color) {
            JPanel box = new JPanel() {
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(100, 100);
                }
            };
            box.setBackground(color);
            return box;
        }

    }

}

自定义绘画

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private List<Box> boxes = new ArrayList<Box>(2);

        public TestPane() {
            boxes.add(new Box(Color.BLUE, 0, 0));
            boxes.add(new Box(Color.RED, 50, 50));

            setLayout(new BorderLayout());

            JButton flip = new JButton("Flip");
            flip.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    // Ok, this is a cheat as I only have to boxes,
                    // if you had more, you'd need to move the box you 
                    // higher in the rendering order closer to the end 
                    // of the list
                    Collections.reverse(boxes);
                    repaint();
                }
            });
            add(flip, BorderLayout.SOUTH);
        }

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

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

            // Cheat, I should looping over the box array to calculate the
            // area the boxes actuall occupy
            int x = (getWidth() - 150) / 2;
            int y = (getHeight() - 150) / 2;

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.translate(x, y);

            for (Box box : boxes) {
                box.paint(g2d);
                x += 50;
                y += 50;
            }
            g2d.dispose();
        }
    }

    public class Box {

        private Color color;

        private int x, y;

        public Box(Color color, int x, int y) {
            this.color = color;
            this.x = x;
            this.y = y;
        }

        public void paint(Graphics2D g2d) {
            g2d.setColor(color);
            g2d.fillRect(x, y, 100, 100);
        }
    }

}

关于java - 自定义绘画和 Z 顺序 : query re painting order,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59525296/

相关文章:

java - Java 中的 repaint() 不会立即返回 "re-paint"吗?

java - 为什么我的 JPanel 会与 JFrame 上的其他组件混淆?

java - gradle 构建期间 Aviary-SDK 错误 (Android)

java - 接口(interface)有两个不同的实现在不同的线程上执行

Java-自定义光标在不同操作系统中是不同的

java - 组合框箭头按钮根据 nimbus LaF 中的字体大小进行拉伸(stretch)

java - POI CellStyle 似乎未应用

java - 当我尝试运行 Jasper Report 时出现错误 "unknown hyperlink target 0"

java - 更改按钮大小 FlowLayout

java - JPanel 无法正确绘制