I am trying to code a custom JPanel that follows the layoutmanager of the containing JPanel (I am using gridbag). My custom JPanel is getting a little too long to paste here, but a checklist of all methods to overwrite to have for this to work.

Here is an excerpt from my custom JPanel:

        public class GridPanel extends JPanel implements MouseMotionListener, MouseListener{
            private Rectangle offdutyRect, sbRect, driveRect, onRect;
            private int delta = 60;
            private int[][] gridArray;
            int draggedStartX = -1;
            int draggedStartY = -1;
            private int dutyStatusSpacing = 60;
            private int totalSpacing = 100;

            public GridPanel(){
            this.setSize(new Dimension(800, 100));
            this.addMouseMotionListener((MouseMotionListener) this);
            this.addMouseListener(this);
            int gridArrayColumns = 24*60/delta;
            gridArray = new int[4][gridArrayColumns];

            int r = 0;
            int rHeight = this.getHeight()/4;
            offdutyRect = new Rectangle(this.getX() + dutyStatusSpacing, this.getY() + r*rHeight, this.getWidth() - totalSpacing, rHeight);
            r++;
            sbRect = new Rectangle(this.getX() + dutyStatusSpacing, this.getY() + r*rHeight, this.getWidth() - totalSpacing, rHeight);
            r++;
            driveRect = new Rectangle(this.getX() + dutyStatusSpacing, this.getY() + r*rHeight, this.getWidth() - totalSpacing, rHeight);
            r++;
            onRect = new Rectangle(this.getX() + dutyStatusSpacing, this.getY() + r*rHeight, this.getWidth() - totalSpacing, rHeight);

            Rectangle rect = null;
            for(r = 0; r < gridArray.length; r++){
                if(r == 0){
                rect = offdutyRect;
                }else if(r == 1){
                rect = sbRect;
                }else if(r == 2){
                rect = driveRect;
                }else if(r == 3){
                rect = onRect;
                }

                //I haven't actually derived any of these things, just my best guesses.
                int len = gridArray[r].length;
                int width = (int) (rect.getWidth()/len);
                rect.setSize((int)(width*len), (int) rect.getHeight());
            }
        }

    public void paintComponent(Graphics g){
        g.clearRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
        //the center black bar for duty status "placeholders"
        g.setColor(Color.black);
        g.drawRect(getX(), getY(), getWidth() - 1, getHeight() - 1);
        g.drawRect((int)offdutyRect.getX(), (int)offdutyRect.getY() + (int)offdutyRect.getHeight()/2, (int)offdutyRect.getWidth(), 1);
        g.drawRect((int)sbRect.getX(), (int)sbRect.getY() + (int)sbRect.getHeight()/2, (int)sbRect.getWidth(), 1);
        g.drawRect((int)driveRect.getX(), (int)driveRect.getY() + (int)driveRect.getHeight()/2, (int)driveRect.getWidth(), 1);
        g.drawRect((int)onRect.getX(), (int)onRect.getY() + (int)onRect.getHeight()/2, (int)onRect.getWidth(), 1);
        g.setColor(Color.pink);
        g.drawRect((int)offdutyRect.getX(), (int)offdutyRect.getY(), (int)offdutyRect.getWidth(), (int)offdutyRect.getHeight());
        g.drawRect((int)sbRect.getX(), (int)sbRect.getY(), (int)sbRect.getWidth(), (int)sbRect.getHeight());
        g.drawRect((int)driveRect.getX(), (int)driveRect.getY(), (int)driveRect.getWidth(), (int)driveRect.getHeight());
        g.drawRect((int)onRect.getX(), (int)onRect.getY(), (int)onRect.getWidth(), (int)onRect.getHeight());

        //draw the array
        g.setColor(Color.green);
        Rectangle rect = null;
        for(int r = 0; r < gridArray.length; r++){
            if(r == 0){
            rect = offdutyRect;
            }else if(r == 1){
            rect = sbRect;
            }else if(r == 2){
            rect = driveRect;
            }else if(r == 3){
            rect = onRect;
            }

            //I haven't actually derived any of these things, just my best guesses.
            int len = gridArray[r].length;
            int width = (int) (rect.getWidth()/len);

            int height = (int) rect.getHeight() - 2;
            for(int c = 0; c < gridArray[r].length; c++){
            if(gridArray[r][c] == 1){
                    int x = (int) (rect.getX() + width*c);
                    int y = (int) rect.getY() + 2;
                    g.fillRect(x, y, width, height);
            }
            }
        }
        }
    //implement setSize
    public void setSize(Dimension d){

    if(gridArray == null){

        int gridArrayColumns = 24*60/delta;
        gridArray = new int[4][gridArrayColumns];
    }
    int r = 0;
    int rHeight = this.getHeight()/4;
    offdutyRect = new Rectangle(this.getX() + dutyStatusSpacing, this.getY() + r*rHeight, (int)d.getWidth() - totalSpacing, rHeight);
    r++;
    sbRect = new Rectangle(this.getX() + dutyStatusSpacing, this.getY() + r*rHeight, (int)d.getWidth() - totalSpacing, rHeight);
    r++;
    driveRect = new Rectangle(this.getX() + dutyStatusSpacing, this.getY() + r*rHeight, (int)d.getWidth() - totalSpacing, rHeight);
    r++;
    onRect = new Rectangle(this.getX() + dutyStatusSpacing, this.getY() + r*rHeight, (int)d.getWidth() - totalSpacing, rHeight);

    Rectangle rect = null;
    for(r = 0; r < gridArray.length; r++){
        if(r == 0){
        rect = offdutyRect;
        }else if(r == 1){
        rect = sbRect;
        }else if(r == 2){
        rect = driveRect;
        }else if(r == 3){
        rect = onRect;
        }

        //I haven't actually derived any of these things, just my best guesses.
        int len = gridArray[r].length;
        int width = (int) (rect.getWidth()/len);
        rect.setSize((int)(width*len), (int) rect.getHeight());
    }
    }

}

And here is the relatively short custom container that multiple instances of my custom class will be added:

public class GridContainer extends JPanel implements ActionListener{
    List<GridPanel> panels = new ArrayList<GridPanel>();

    public GridContainer(){
    GridBagLayout gb = new GridBagLayout();
    this.setLayout(gb);
    GridBagConstraints c = new GridBagConstraints();

    c.gridx = 0;
    c.gridy = 0;
    c.weightx = .5;
    c.weighty = .5;
    c.fill = GridBagConstraints.HORIZONTAL;
    BufferedImage img = null;
    try {
        img = ImageIO.read(this.getClass().getResource("/res/add.png"));
    } catch (IOException e) { }
    JButton addButton = new JButton();
    addButton.setIcon(new ImageIcon(img));
    addButton.addActionListener(this);
    this.add(addButton, c);
    }

    public void actionPerformed(ActionEvent e) {
    System.out.println("action performed"  +  panels.size());
    this.setLayout(new GridBagLayout());
    removeAll();
    repaint();
    panels.add(new GridPanel());
    int r = 0;
    GridBagConstraints c = new GridBagConstraints();
    for(int i = 0; i < panels.size(); i++){
            JLabel label = new JLabel("day " + i);
            c = new GridBagConstraints();
            c.gridx = 0;
            c.gridy = r;
            c.weightx = 1;
            c.weighty = 1;
            this.add(label, c);
            r++;

            c = new GridBagConstraints();
            c.gridx = 0;
            c.gridy = r;
            c.weightx = 1;
            c.weighty = 1;
            c.fill = GridBagConstraints.HORIZONTAL;
            this.add(panels.get(i), c);
            r++;
    }

    c = new GridBagConstraints();
    c.gridx = 0;
    c.gridy = r;
    c.weightx = 1;
    c.weighty = 1;
    c.fill = GridBagConstraints.HORIZONTAL;
    BufferedImage img = null;
    try {
        img = ImageIO.read(this.getClass().getResource("/res/add.png"));
    } catch (IOException ex) { }
    JButton addButton = new JButton();
    addButton.setIcon(new ImageIcon(img));
    addButton.addActionListener(this);
    this.add(addButton, c);
    repaint();

    }
}

Don't feel obligated to debug this wall of code, just some context to where I'm at.

有帮助吗?

解决方案 2

  • A component should provide, at the very least, a preferredSize for the parent layout manager to determine how best it should be laid out. You should not be making calculations about your size expect to meet these requirements...
  • When painting, your component's graphics context has already begin translated to the x/y position of your component. This means that the top/left corner of your component is actually 0x0
  • You should be calling super.paintComponent. This is very important as it prepares the graphics context for painting, this doubly important when dealing with transparent components

enter image description here

public class GridPanel extends JPanel implements MouseMotionListener, MouseListener {

    private Rectangle offdutyRect, sbRect, driveRect, onRect;
    private int delta = 60;
    private int[][] gridArray;
    int draggedStartX = -1;
    int draggedStartY = -1;
    private int dutyStatusSpacing = 60;
    private int totalSpacing = 100;

    public GridPanel() {
        setBackground(Color.WHITE);
        //this.setSize(new Dimension(800, 100));
        this.addMouseMotionListener((MouseMotionListener) this);
        this.addMouseListener(this);
        int gridArrayColumns = 24 * 60 / delta;
        gridArray = new int[4][gridArrayColumns];

        int r = 0;
        int rHeight = this.getHeight() / 4;
        offdutyRect = new Rectangle(dutyStatusSpacing, r * rHeight, this.getWidth() - totalSpacing, rHeight);
        r++;
        sbRect = new Rectangle(dutyStatusSpacing, r * rHeight, this.getWidth() - totalSpacing, rHeight);
        r++;
        driveRect = new Rectangle(dutyStatusSpacing, r * rHeight, this.getWidth() - totalSpacing, rHeight);
        r++;
        onRect = new Rectangle(dutyStatusSpacing, r * rHeight, this.getWidth() - totalSpacing, rHeight);

        Rectangle rect = null;
        for (r = 0; r < gridArray.length; r++) {
            if (r == 0) {
                rect = offdutyRect;
            } else if (r == 1) {
                rect = sbRect;
            } else if (r == 2) {
                rect = driveRect;
            } else if (r == 3) {
                rect = onRect;
            }

            //I haven't actually derived any of these things, just my best guesses.
            int len = gridArray[r].length;
            int width = (int) (rect.getWidth() / len);
            rect.setSize((int) (width * len), (int) rect.getHeight());
        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        //the center black bar for duty status "placeholders"
        g.setColor(Color.black);
        g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
        g.drawRect((int) offdutyRect.getX(), (int) offdutyRect.getY() + (int) offdutyRect.getHeight() / 2, (int) offdutyRect.getWidth(), 1);
        g.drawRect((int) sbRect.getX(), (int) sbRect.getY() + (int) sbRect.getHeight() / 2, (int) sbRect.getWidth(), 1);
        g.drawRect((int) driveRect.getX(), (int) driveRect.getY() + (int) driveRect.getHeight() / 2, (int) driveRect.getWidth(), 1);
        g.drawRect((int) onRect.getX(), (int) onRect.getY() + (int) onRect.getHeight() / 2, (int) onRect.getWidth(), 1);
        g.setColor(Color.pink);
        g.drawRect((int) offdutyRect.getX(), (int) offdutyRect.getY(), (int) offdutyRect.getWidth(), (int) offdutyRect.getHeight());
        g.drawRect((int) sbRect.getX(), (int) sbRect.getY(), (int) sbRect.getWidth(), (int) sbRect.getHeight());
        g.drawRect((int) driveRect.getX(), (int) driveRect.getY(), (int) driveRect.getWidth(), (int) driveRect.getHeight());
        g.drawRect((int) onRect.getX(), (int) onRect.getY(), (int) onRect.getWidth(), (int) onRect.getHeight());

        //draw the array
        g.setColor(Color.green);
        Rectangle rect = null;
        for (int r = 0; r < gridArray.length; r++) {
            if (r == 0) {
                rect = offdutyRect;
            } else if (r == 1) {
                rect = sbRect;
            } else if (r == 2) {
                rect = driveRect;
            } else if (r == 3) {
                rect = onRect;
            }

            //I haven't actually derived any of these things, just my best guesses.
            int len = gridArray[r].length;
            int width = (int) (rect.getWidth() / len);

            int height = (int) rect.getHeight() - 2;
            for (int c = 0; c < gridArray[r].length; c++) {
                if (gridArray[r][c] == 1) {
                    int x = (int) (rect.getX() + width * c);
                    int y = (int) rect.getY() + 2;
                    g.fillRect(x, y, width, height);
                }
            }
        }
    }

    //implement setSize
    //        public void setSize(Dimension d) {
    @Override
    public void doLayout() {

        if (gridArray == null) {

            int gridArrayColumns = 24 * 60 / delta;
            gridArray = new int[4][gridArrayColumns];
        }
        int r = 0;
        int rHeight = this.getHeight() / 4;
        offdutyRect = new Rectangle(dutyStatusSpacing, r * rHeight, (int) getWidth() - totalSpacing, rHeight);
        r++;
        sbRect = new Rectangle(dutyStatusSpacing, r * rHeight, (int) getWidth() - totalSpacing, rHeight);
        r++;
        driveRect = new Rectangle(dutyStatusSpacing, r * rHeight, (int) getWidth() - totalSpacing, rHeight);
        r++;
        onRect = new Rectangle(dutyStatusSpacing, r * rHeight, (int) getWidth() - totalSpacing, rHeight);

        Rectangle rect = null;
        for (r = 0; r < gridArray.length; r++) {
            if (r == 0) {
                rect = offdutyRect;
            } else if (r == 1) {
                rect = sbRect;
            } else if (r == 2) {
                rect = driveRect;
            } else if (r == 3) {
                rect = onRect;
            }

            //I haven't actually derived any of these things, just my best guesses.
            int len = gridArray[r].length;
            int width = (int) (rect.getWidth() / len);
            rect.setSize((int) (width * len), (int) rect.getHeight());
        }
    }

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

    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }
}

I highly recommend that you take a look at Performing Custom Painting

其他提示

I am trying to code a custom JPanel that follows the layoutmanager of the containing JPanel

A component does not care what panel it is added to, so it is also does not care what layout manager that the parent panel uses.

A custom component overrides the getPreferredSize(), getMinimumSize() and getMaximumSize() methods so that the layout manager of the parent container can use these suggestions to determine the actual size of the component.

Then in your paintComponent() method use use the getWidth() and geHeight() methods to determine the actual size of your component. Then you paint your component accordingly. Your logic needs to determine how to paint your component based on the eventual size.

g.clearRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());

Your painting code should be using (0, 0) for the X/Y location since you are trying to clear the entire area of your panel. The getX()/getY() values may not be zero because the layout manager of the parent container will determine the location of the panel.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top