Home » Java » java – Packing Polygons and Jbutton into Tile-Exceptionshub

java – Packing Polygons and Jbutton into Tile-Exceptionshub

Posted by: admin February 25, 2020 Leave a comment

Questions:

I am making Mahjong game in swing for course project and i am trying to create a Tile, which is actually a simple JButton with some Polygons connected to it. It looks like that (https://imgur.com/af7zzTn).

I created a method, that creates the structure like above, but i have 2 problems:

  1. I dont know how to “pack” structure above into “tile”. Lets say, “tile” will be the object of “Tile” class that extends JButton, or JComponent, i’m not sure which one suits it better.

  2. I want to add tiles like above to JLayeredPane, which will allow me, to use 3rd coordinate, like “z”, or in other words, to put tiles on top of each other without bugging them, but i also dont know how to do it.

Could you please help me? I am so confused, I spent like 10 hours already fixing a problem like that, but i couldn’t find the best solution on the internet. Also i would be happy, if you will make the answer as simply as possible.

Thank you in advance and here is my code:

public class TestWindow extends JFrame {
static JLayeredPane levels;

public TestWindow() {
    super("Test");
    PanelTest panel = new PanelTest();
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setSize(1000, 700);
    setVisible(true);
    add(panel);
    levels = getLayeredPane();
    System.out.println(levels.getSize());
}


//Method for creating Tile, like one on the image
public static void createTestTile(int x, int y, int z, Graphics g) {
    Graphics2D g2d = (Graphics2D) g;

    //Left
    Polygon ml = new Polygon();
    ml.addPoint(x, y);
    ml.addPoint(x - 5, y + 10);
    ml.addPoint(x - 5, y + 70);
    ml.addPoint(x, y + 60);
    g2d.setPaint(Color.LIGHT_GRAY);
    g2d.fillPolygon(ml);
    g2d.setPaint(Color.GRAY);
    g2d.drawLine(x, y + 60, x - 5, y + 69);

    //Middle-bottom
    Polygon mb = new Polygon();
    mb.addPoint(x, y + 60);
    mb.addPoint(x - 5, y + 70);
    mb.addPoint(x + 41, y + 70);
    mb.addPoint(x + 46, y + 60);
    g2d.setPaint(Color.LIGHT_GRAY);
    g2d.fillPolygon(mb);

    //JButton on top
    JButton top = new JButton();
    top.setBounds(x, y, 46, 60);
    TestWindow.levels.add(top, new Integer(z));
}

class PanelTest extends JPanel {


    int x;
    int y;

    public PanelTest() {
        setLayout(null);


    }

    public void paintComponent(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;

        createTestTile(200, 100, 1, g);
        createTestTile(200, 160, 1, g);
        createTestTile(246, 160, 1, g);
        createTestTile(223, 130, 2, g);
        createTestTile(246, 100, 1, g);
        createTestTile(400, 100, 1, g);
    }

}

public static void main(String[] args) {
    TestWindow window = new TestWindow();
}

}

How to&Answers:

Painting code is for painting only. It should NEVER create a component.

For a game you have two design approaches:

  1. Use real components and customize the components by using features like Borders.
  2. do custom painting for everything.

Your code is trying to mix both approaches, which is incorrect.

i am trying to create a Tile, which is actually a simple JButton with some Polygons connected to it.

If you want to implement the game using real components then you should implement the look you want by using a custom Border to give the button the 3D effect.

Read the section from the Swing tutorial on How to Use Borders

Below is an example that I modified based on the “raised” BevelBorderto paint highlights only on two sides:

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

/**
 * A class which implements a simple tile border.
 */
public class TileBorder extends AbstractBorder
{
    protected int thickness;
    protected Color shadowInner;
    protected Color shadowOuter;

    /**
     * Creates a tile border with the specified type and whose
     * colors will be derived from the background color of the
     * component passed into the paintBorder method.
     * @param thickness the type of tile for the border
     */
    public TileBorder(int thickness) {
        this.thickness = thickness;
    }

    /**
     * Creates a tile border with the specified type, highlight and
     * shadow colors.
     * @param thickness the type of tile for the border
     * @param shadow the color to use for the tile shadow
     */
    public TileBorder(int thickness, Color shadow)
    {
        this(thickness, shadow, shadow.brighter());
    }

    /**
     * Creates a tile border with the specified type, highlight and
     * shadow colors.
     *
     * @param thickness the type of tile for the border
     * @param shadowOuterColor the color to use for the tile outer shadow
     * @param shadowInnerColor the color to use for the tile inner shadow
     */
    public TileBorder(int thickness, Color shadowOuterColor, Color shadowInnerColor)
    {
        this(thickness);
        this.shadowOuter = shadowOuterColor;
        this.shadowInner = shadowInnerColor;
    }

    /**
     * Paints the border for the specified component with the specified
     * position and size.
     * @param c the component for which this border is being painted
     * @param g the paint graphics
     * @param x the x position of the painted border
     * @param y the y position of the painted border
     * @param width the width of the painted border
     * @param height the height of the painted border
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
    {
        int h = height;
        int w = width;
        int t = thickness;
        int m = thickness / 2;

        Graphics2D g2d = (Graphics2D)g.create();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.translate(x, y);

        //  Paint border on right/bottom of component

        Shape outer = new Rectangle(0, 0, w - 1, h - 1);
        Shape inner = new Rectangle(0, 0, w - t, h - t);

        Area border = new Area( outer );
        border.subtract( new Area(inner) );

        g2d.setColor(getShadowInnerColor(c));
        g2d.fill( border );

        // Highlight border edge

        g2d.setColor(getShadowOuterColor(c));

        g2d.draw( border );
        g2d.drawLine(0, 0, w - t, 0);
        g2d.drawLine(0, 0, 0, h - t);
        g2d.drawLine(w - t, h - t, w, h);

        //  Fill border edge with background color of parent component

        g2d.setColor( c.getParent().getBackground() );

        Polygon topRight = new Polygon();
        topRight.addPoint(w - t, 0);
        topRight.addPoint(w, 0);
        topRight.addPoint(w, m);
        g2d.fillPolygon(topRight);

        Polygon bottomLeft = new Polygon();
        bottomLeft.addPoint(0, h - t);
        bottomLeft.addPoint(0, h);
        bottomLeft.addPoint(m, h);
        g2d.fillPolygon(bottomLeft);

        //  Cleanup

        g2d.dispose();
    }

    /**
     * Reinitialize the insets parameter with this Border's current Insets.
     * @param c the component for which this border insets value applies
     * @param insets the object to be reinitialized
     */
    public Insets getBorderInsets(Component c, Insets insets)
    {
        insets.set(0, 0, thickness, thickness);
        return insets;
    }

    /**
     * Returns the inner shadow color of the tile border
     * when rendered on the specified component.  If no shadow
     * color was specified at instantiation, the shadow color
     * is derived from the specified component's background color.
     * @param c the component for which the shadow may be derived
     */
    public Color getShadowInnerColor(Component c)
    {
        Color shadow = getShadowInnerColor();
        return shadow != null ? shadow : c.getBackground().darker();
    }

    /**
     * Returns the outer shadow color of the tile border
     * when rendered on the specified component.  If no shadow
     * color was specified at instantiation, the shadow color
     * is derived from the specified component's background color.
     * @param c the component for which the shadow may be derived
     */
    public Color getShadowOuterColor(Component c)
    {
        Color shadow = getShadowOuterColor();
        return shadow != null ? shadow : c.getBackground().darker().darker();
    }

    /**
     * Returns the inner shadow color of the tile border.
     * Will return null if no shadow color was specified
     * at instantiation.
     */
    public Color getShadowInnerColor()
    {
        return shadowInner;
    }

    /**
     * Returns the outer shadow color of the tile border.
     * Will return null if no shadow color was specified
     * at instantiation.
     */
    public Color getShadowOuterColor()
    {
        return shadowOuter;
    }

    /**
     * Returns whether or not the border is opaque.
     */
    public boolean isBorderOpaque()
    {
        return true;
    }

    private static void createAndShowGUI()
    {
        Border tile = new TileBorder(6);
        JPanel panel = new JPanel( new FlowLayout(FlowLayout.LEFT, 0, 0) );

        for (int i = 0; i < 5; i++)
        {
            JButton button = new JButton();
            button.setPreferredSize( new Dimension(50, 100) );
            button.setBorder( tile );
            panel.add( button );
        }

        JLayeredPane layeredPane = new JLayeredPane();
        int locationX = 20;
        int locationY = 50;

        for (int i = 0; i < 5; i++)
        {
            JButton button = new JButton();
            button.setSize( new Dimension(50, 100) );
            button.setLocation(locationX, locationY);
            button.setBorder( tile );
            panel.add( button );
            layeredPane.add(button, new Integer(i));
            locationX += 6;
            locationY -= 6;
        }

        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(panel, BorderLayout.PAGE_START);
        frame.add(layeredPane, BorderLayout.CENTER);
        frame.setSize(300, 300);
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args) throws Exception
    {
        java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
    }
}

It allows you to customize the thickness of the Border as well as specify border colors.

You can also check out How to Use Layered Panes for working examples.