[RESOLVIDO] super.paintComponent() não funciona como deveria

Boa tarde,

Estou a tentar implementar algo muito simples: desenhar a mesma forma geométrica no ecrã em vários sitios diferentes, para dar a sensação de movimento.

O problema que está a acontecer é que, fazendo override ao paintComponent() e, lá dentro, invocando o super.paintComponent(), este não está a redesenhar o background do JPanel como deveria. E, assim, sendo, fico sempre com cada forma geométrica desenhada no ecrã e visivel, o que não é pretendido (o que é pretendido é que apenas seja visivel, a cada momento, a última forma geométrica desenhada, dando a sensação de movimento).

Segue o código: (não envio a classe CustomShapeButton por ser desnecessária para esta análise)

public class DBLauncher extends JApplet
{
    public static void main(String args[]) 
    {
        JFrame frame = new JFrame();
        
        frame.setTitle("Database Launcher v1.0");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        JApplet applet = new DBLauncher();
        
        applet.init();
        frame.getContentPane().add(applet);
        frame.pack();
        frame.setLocation(new Point(430, 150));
        frame.setVisible(true);
        frame.setResizable(false);
        frame.setSize(500, 500);
    }
    
    @Override
    public void init()
    {
        DBPanel panel = null;
        
        try 
        {
            panel = new DBPanel();
        } 
        catch (IOException ex) 
        {
            Logger.getLogger(DBLauncher.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        getContentPane().add(panel);
        
        panel.setBackground(Color.lightGray);
    }
}

class DBPanel extends JPanel implements Runnable, MouseListener
{   
    int musicShapePosX = 85;
    int musicShapePosY = 100;
    int tvShapePosX = 184;
    int tvShapePosY = 164;
    int gamesShapePosX = 283;
    int gamesShapePosY = 100;
    int booksShapePosX = 283;
    int booksShapePosY = 228;
    int techShapePosX = 184;
    int techShapePosY = 293;
    int SEGMENT_SHAPE_LENGTH = 50;
    int SHAPE_HEIGHT = 10;
    int IMAGE_FACTOR_X = 1 ;
    int IMAGE_FACTOR_Y = 49;
    
    int musicImagePosX = musicShapePosX + IMAGE_FACTOR_X;
    int musicImagePosY = musicShapePosY - IMAGE_FACTOR_Y;
    int tvImagePosX = tvShapePosX + IMAGE_FACTOR_X;
    int tvImagePosY = tvShapePosY - IMAGE_FACTOR_Y;
    int gamesImagePosX = gamesShapePosX + IMAGE_FACTOR_X;
    int gamesImagePosY = gamesShapePosY - IMAGE_FACTOR_Y;
    int booksImagePosX = booksShapePosX + IMAGE_FACTOR_X;
    int booksImagePosY = booksShapePosY - IMAGE_FACTOR_Y;
    int techImagePosX = techShapePosX + IMAGE_FACTOR_X;
    int techImagePosY = techShapePosY - IMAGE_FACTOR_Y;
    
    float SHAPE_SPEED = 7.5f; 
    
    CustomShapeButton musicShapeButton = new CustomShapeButton(musicShapePosX, musicShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
    CustomShapeButton tvShapeButton = new CustomShapeButton(tvShapePosX, tvShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
    CustomShapeButton gamesShapeButton = new CustomShapeButton(gamesShapePosX, gamesShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
    CustomShapeButton booksShapeButton = new CustomShapeButton(booksShapePosX, booksShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
    CustomShapeButton techShapeButton = new CustomShapeButton(techShapePosX, techShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
    
    private static BufferedImage musicShapeImage;
    private static BufferedImage tvShapeImage;
    private static BufferedImage gamesShapeImage;
    private static BufferedImage techShapeImage;
    private static BufferedImage booksShapeImage;
    
    private ArrayList<Shape> shapes = null;
    
    boolean musicButtonPressed = false;
    boolean tvButtonPressed = false;
    boolean gamesButtonPressed = false;
    boolean booksButtonPressed = false;
    boolean techButtonPressed = false;
    
    @SuppressWarnings("CallToThreadStartDuringObjectConstruction")
    public DBPanel() throws IOException
    {       
        shapes = new ArrayList();
        
        shapes.add(musicShapeButton);
        shapes.add(tvShapeButton);
        shapes.add(gamesShapeButton);
        shapes.add(booksShapeButton);
        shapes.add(techShapeButton);
        
        try
        {
            musicShapeImage = ImageIO.read(getClass().getResource("/images/imageWoodMusic.png"));
            tvShapeImage = ImageIO.read(getClass().getResource("/images/imageWoodTv.png"));
            gamesShapeImage = ImageIO.read(getClass().getResource("/images/imageWoodGames.png"));
            booksShapeImage = ImageIO.read(getClass().getResource("/images/imageWoodBooks.png"));
            techShapeImage = ImageIO.read(getClass().getResource("/images/imageWoodTech.png"));
            
        }
        catch (IOException ioe)
        {
            ioe.printStackTrace();
        } 
        
        this.setFocusable(true);
        addMouseListener(this); //Mouse events listener
        
        //Create and initialize the animation thread
        Thread thread = new Thread(this);
        
        thread.start();
    }
    
    @Override
    public void paintComponent(Graphics g) 
    {
        super.paintComponent(g);
        
        Graphics2D g2 = (Graphics2D) g;
        
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        g2.draw(musicShapeButton);
       // g2.draw(tvShapeButton);
       // g2.draw(gamesShapeButton);
       // g2.draw(booksShapeButton);
       // g2.draw(techShapeButton);
        
       // g2.drawImage(musicShapeImage, musicImagePosX, musicImagePosY, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, null);
       // g2.drawImage(tvShapeImage, tvImagePosX, tvImagePosY, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, null);
       // g2.drawImage(gamesShapeImage, gamesImagePosX, gamesImagePosY, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, null);
       // g2.drawImage(booksShapeImage, booksImagePosX, booksImagePosY, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, null);
       // g2.drawImage(techShapeImage, techImagePosX, techImagePosY, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, null);
    }
    
    @Override
    @SuppressWarnings("SleepWhileInLoop")
    public void run() 
    {
        while (true)
        {
            if (musicButtonPressed == true)
            {
                animateButtonShapeMusic();
            }
            else if (tvButtonPressed == true)
            {
                
            }
            else if (gamesButtonPressed == true)
            {
                
            }
            else if (booksButtonPressed == true)
            {
                
            }
            else if (techButtonPressed == true)
            {
                
            }
        
            repaint();

            try
            {
                Thread.sleep(35);
            } 
            catch (InterruptedException ex)
            {
                break;
            }
        }
    }
    
    public void delay(int milliseconds)
    {
        try
        {
            Thread.sleep(milliseconds);
        } 
        catch (InterruptedException e)
        {
        }
    }

    @Override
    public void mouseClicked(MouseEvent me) 
    {
        for (int i = 0; i < shapes.size(); i++)
        {
            Shape shape = shapes.get(i);
            
            //Mostrar os icones respetivos, consoante o botão seleccionado (if's)
            
            if (shape.contains(me.getPoint()))
            {
                if (shape.equals(i) == shape.equals(0))
                {
                    musicButtonPressed = true;
                    
                    System.out.println("Clicked on shape number " + i + " that represents the music button!");
                }
                else if (shape.equals(i) == shape.equals(1))
                {
                    tvButtonPressed = true;
                    
                    System.out.println("Clicked on shape number " + i + " that represents the tv button!");
                }    
                else if (shape.equals(i) == shape.equals(2))
                {
                    gamesButtonPressed = true;
                    
                    System.out.println("Clicked on shape number " + i + " that represents the games button!");
                }
                else if (shape.equals(i) == shape.equals(3))
                {
                    booksButtonPressed = true;
                    
                    System.out.println("Clicked on shape number " + i + " that represents the books button!");
                }
                else if (shape.equals(i) == shape.equals(4))
                {
                    techButtonPressed = true;
                    
                    System.out.println("Clicked on shape number " + i + " that represents the tech button!");
                }
            }
        }
    }
    
    public void animateButtonShapeMusic()
    {
        if (musicShapePosY < 228)
        {
            musicShapePosY = (int)(musicShapePosY + SHAPE_SPEED);    
            
            musicShapeButton.drawShape(musicShapePosX, musicShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
        }
    }
    
    public void animateButtonShapeTv()
    {    
    }
    
    public void animateButtonShapeGames()
    {     
    }
    
    public void animateButtonShapeBooks()
    {     
    }
    
    public void animateButtonShapeTech()
    {    
    }

    @Override
    public void mousePressed(MouseEvent me) 
    { 
    }

    @Override
    public void mouseReleased(MouseEvent me) 
    {   
    }

    @Override
    public void mouseEntered(MouseEvent me) 
    {   
    }

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

Obrigado e cumprimentos,
J Amorim

Desculpem, mas o post não ficou bem formatado na visualização do código-fonte.

Edite o post, selecione o código fonte e clique no botão de texto pré formatado identificado pelo símbolo </>

1 curtida

O JPanel só pinta a cor de fundo quando sua propriedade opaque for true.
No construtor de seu DBPanel faça setOpaque(true).

Outra alternativa, talvez mais performática seria você não chamar o super no método paintComponent e, ao invés disso, simplesmente preencher o JPanel com a cor de fundo.

Exemplo:

@Override
public void paintComponent(Graphics g) {

    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    
    g2.setColor(getBackground()); // setar cor de fundo do objeto Graphics2D
    g2.fillRect(0, 0, getWidth(), getHeight()); // preencher com cor de fundo


    g2.draw(musicShapeButton);
    // g2.draw(tvShapeButton);
    // g2.draw(gamesShapeButton);
    // g2.draw(booksShapeButton);
    // g2.draw(techShapeButton);

    // g2.drawImage(musicShapeImage, musicImagePosX, musicImagePosY, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, null);
    // g2.drawImage(tvShapeImage, tvImagePosX, tvImagePosY, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, null);
    // g2.drawImage(gamesShapeImage, gamesImagePosX, gamesImagePosY, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, null);
    // g2.drawImage(booksShapeImage, booksImagePosX, booksImagePosY, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, null);
    // g2.drawImage(techShapeImage, techImagePosX, techImagePosY, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, null);
}
1 curtida

Obrigado staroski pelas dicas. No entanto, tentei ambas as soluções que você deu, mas infelizmente sem sucesso.

Quando insiro a instrução this.setOpaque(true); no construtor DBPanel(), imediatamente antes da instrução this.setFocusable(true); o comportamento inesperado é o mesmo, ou seja, a shape continua a ser desenhada múltiplas vezes no JPanel, de forma visivel.

Quando insiro as instruções g2.setColor(getBackground()); e g2.fillRect(0, 0, getWidth(), getHeight()); no override do paintComponent(Graphics g), e comento a instrução super.paintComponent(g);, simplesmente nada é desenhado no JPanel, apenas vejo a cor de fundo, não vejo a shape em momento algum. No entanto eu sei que ela está lá desenhada, porque se clicar na sua zona, obtenho o texto de debug respetivo que fiz para o efeito.

Estará faltando algo? Ou estará alguma instrução a mais/em falta no init(); ou no main();?

Obrigado.

PS - Revi todo o código para o simplificar ao máximo. Substituí o JApplet pelo JFrame, como deve ser feito hoje em dia. Mesmo assim continuo a não conseguir. :frowning:

public class Animated_Shape_Test extends JFrame
{
    @SuppressWarnings("ResultOfObjectAllocationIgnored")
    public static void main(String args[]) throws IOException 
    {
        new Animated_Shape_Test();
    }
    
    public Animated_Shape_Test() throws IOException
    {
        this.setTitle("Database Launcher v1.0");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocation(new Point(430, 150));
        this.setResizable(false);
        this.setSize(500, 500);
        this.add(new DBPanel(), BorderLayout.CENTER);
        this.setVisible(true);
    }
}

class DBPanel extends JPanel implements Runnable
{   
    int musicShapePosX = 85;
    int musicShapePosY = 100;
    int SEGMENT_SHAPE_LENGTH = 50;
    int SHAPE_HEIGHT = 10;
    float SHAPE_SPEED = 7.5f; 
    
    CustomShapeButton musicShapeButton = new CustomShapeButton(musicShapePosX, musicShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
    
    private ArrayList<Shape> shapes = null;
    
    @SuppressWarnings("CallToThreadStartDuringObjectConstruction")
    public DBPanel() throws IOException
    {       
        shapes = new ArrayList();
        
        shapes.add(musicShapeButton);
        
        this.setOpaque(true);
        this.setFocusable(true);
        
        Thread thread = new Thread(this);
        
        thread.start();
    }
    
    @Override
    public void paintComponent(Graphics g) 
    {
        //super.paintComponent(g);
        
        Graphics2D g2 = (Graphics2D) g;
        
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  
        g2.setColor(getBackground().darker());
        g2.fillRect(0, 0, getWidth(), getHeight());
        
        g2.setColor(Color.YELLOW);
        g2.draw(musicShapeButton);
    }
    
    public void delay(int milliseconds)
    {
        try
        {
            Thread.sleep(milliseconds);
        } 
        catch (InterruptedException e)
        {
        }
    }
     
    @Override
    public void run() 
    {
        while (true)
        {
            animateButtonShapeMusic();
            delay(35);
            repaint();
        }
    }
    
    public void animateButtonShapeMusic()
    {
        if (musicShapePosY < 228)
        {
            musicShapePosY = (int)(musicShapePosY + SHAPE_SPEED);    
            
            musicShapeButton.drawShape(musicShapePosX, musicShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
        }
    }
}

Posta o código fonte da classe CustomShapeButton, de modo que seja possível eu executar seu código e analisar.

1 curtida

Aqui está ele:

public final class CustomShapeButton extends JButton implements Shape
{   
    GeneralPath path = new GeneralPath();

    public CustomShapeButton(float posX, float posY, float segmentLength, float shapeHeight)
    {    
        drawShape(posX, posY, segmentLength, shapeHeight);
    }
    
    public void drawShape(float posX, float posY, float segmentLength, float shapeHeight)
    {   
        float x0 = posX;
        float y0 = posY + shapeHeight;
        float x1 = posX - 3 + (segmentLength * 0.65f);
        float y1 = posY - segmentLength;
        float x2 = x1 - 6 + segmentLength * 1.35f;
        float y2 = y1;
        float x3 = x2 - 3 + (segmentLength * 0.65f);
        float y3 = y2 + shapeHeight + segmentLength;
        float x4 = x3 + 3 - (segmentLength * 0.65f);
        float y4 = y3 + shapeHeight + segmentLength;
        float x5 = x4 + 6 - (segmentLength * 1.35f);
        float y5 = y4;
        
        path.moveTo(x0, y0);
        path.lineTo(x1, y1);
        path.lineTo(x2, y2);
        path.lineTo(x3, y3);
        path.lineTo(x4, y4);
        path.lineTo(x5, y5);
        path.closePath();
    }

    @Override
    public Rectangle2D getBounds2D() 
    {
        return path.getBounds2D();
    }

    @Override
    public boolean contains(double d, double d1) 
    {
        return path.contains(d, d1);
    }

    @Override
    public boolean contains(Point2D pd) 
    {
        return path.contains(pd);
    }

    @Override
    public boolean intersects(double d, double d1, double d2, double d3) 
    {
        return path.intersects(d, d1, d2, d3);
    }

    @Override
    public boolean intersects(Rectangle2D rd) 
    {
        return path.intersects(rd);
    }

    @Override
    public boolean contains(double d, double d1, double d2, double d3) 
    {
        return path.contains(d, d1, d2, d3);
    }

    @Override
    public boolean contains(Rectangle2D rd) 
    {
        return path.contains(rd);
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at) 
    {
        return path.getPathIterator(at);
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at, double d) 
    {
        return path.getPathIterator(at, d);
    }
}

Obrigado mais uma vez.

O problema da sua classe CustomShapeButton é a implementação do método drawShape
Este método era responsável por adicionar os pontos que formam seu polígono.
O problema é que no seu DBPanel você chama esse método várias vezes enquanto a Thread executa, ou seja, você estava tentando utilizar ele para mover seu polígono.
Isso fazia com que mais e mais pontos fossem adicionados à ele, te dando a falsa impressão de que estava desenhando mais polígonos na tela.

Dê uma olhada nas alterações que fiz em suas classes:

Classe CustomShapeButton:

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JButton;

@SuppressWarnings("serial")
public final class CustomShapeButton extends JButton implements Shape {

    private GeneralPath path = new GeneralPath();

    public CustomShapeButton(float posX, float posY, float segmentLength, float shapeHeight) {
        float x0 = posX;
        float y0 = posY + shapeHeight;
        float x1 = posX - 3 + (segmentLength * 0.65f);
        float y1 = posY - segmentLength;
        float x2 = x1 - 6 + segmentLength * 1.35f;
        float y2 = y1;
        float x3 = x2 - 3 + (segmentLength * 0.65f);
        float y3 = y2 + shapeHeight + segmentLength;
        float x4 = x3 + 3 - (segmentLength * 0.65f);
        float y4 = y3 + shapeHeight + segmentLength;
        float x5 = x4 + 6 - (segmentLength * 1.35f);
        float y5 = y4;

        path.moveTo(x0, y0);
        path.lineTo(x1, y1);
        path.lineTo(x2, y2);
        path.lineTo(x3, y3);
        path.lineTo(x4, y4);
        path.lineTo(x5, y5);
        path.closePath();
    }

    @Override
    public Rectangle2D getBounds2D() {
        return path.getBounds2D();
    }

    @Override
    public boolean contains(double d, double d1) {
        return path.contains(d, d1);
    }

    @Override
    public boolean contains(Point2D pd) {
        return path.contains(pd);
    }

    @Override
    public boolean intersects(double d, double d1, double d2, double d3) {
        return path.intersects(d, d1, d2, d3);
    }

    @Override
    public boolean intersects(Rectangle2D rd) {
        return path.intersects(rd);
    }

    @Override
    public boolean contains(double d, double d1, double d2, double d3) {
        return path.contains(d, d1, d2, d3);
    }

    @Override
    public boolean contains(Rectangle2D rd) {
        return path.contains(rd);
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at) {
        return path.getPathIterator(at);
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at, double d) {
        return path.getPathIterator(at, d);
    }

    public void translate(float distanceX, float distanceY) {
        AffineTransform translation = new AffineTransform();
        translation.translate(distanceX, distanceY);
        path = (GeneralPath) path.createTransformedShape(translation);
    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(getBackground());
        g2d.fill(path);
        g2d.setColor(getForeground());
        g2d.draw(path);
    }
}

Classe DBPanel:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;

@SuppressWarnings("serial")
class DBPanel extends JPanel implements Runnable {

    private int musicShapePosX = 85;
    private int musicShapePosY = 100;
    private int SEGMENT_SHAPE_LENGTH = 50;
    private int SHAPE_HEIGHT = 10;
    private float SHAPE_SPEED = 7.5f;

    private CustomShapeButton musicShapeButton;

    private List<CustomShapeButton> shapeButtons;

    public DBPanel() throws IOException {

        musicShapeButton = new CustomShapeButton(musicShapePosX, musicShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
        musicShapeButton.setBackground(Color.RED);
        musicShapeButton.setForeground(Color.ORANGE);

        shapeButtons = new ArrayList<>();
        shapeButtons.add(musicShapeButton);

        this.setOpaque(true);
        this.setFocusable(true);

        Thread thread = new Thread(this);
        thread.start();
    }

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

        Graphics2D g2 = (Graphics2D) g;

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2.setColor(getBackground().darker());
        g2.fillRect(0, 0, getWidth(), getHeight());

        for (CustomShapeButton shapeButton : shapeButtons) {
            shapeButton.paintComponent(g2);
        }
    }

    public void delay(int milliseconds) {
        try {
            Thread.sleep(milliseconds);
        } catch (InterruptedException e) {}
    }

    @Override
    public void run() {
        while (true) {
            animateButtonShapeMusic();
            delay(50);
            repaint();
        }
    }

    public void animateButtonShapeMusic() {
        if (musicShapePosY < 228) {
            musicShapePosY += SHAPE_SPEED;
            float distanceX = 0;
            float distanceY = SHAPE_SPEED;
            musicShapeButton.translate(distanceX, distanceY);
        }
    }
}

Classe Animated_Shape_Test:

import java.awt.BorderLayout;
import java.awt.Point;
import java.io.IOException;

import javax.swing.JFrame;
import javax.swing.WindowConstants;

@SuppressWarnings("serial")
public class Animated_Shape_Test extends JFrame {

    public static void main(String args[]) throws IOException {
        Animated_Shape_Test frame = new Animated_Shape_Test();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public Animated_Shape_Test() throws IOException {
        this.setTitle("Database Launcher v1.0");
        this.setLocation(new Point(430, 150));
        this.setResizable(false);
        this.setSize(500, 500);
        this.add(new DBPanel(), BorderLayout.CENTER);
    }
}

Muito obrigado staroski, pela sua ajuda.

Agora funcionou, sem dúvida. No entanto fiquei com algumas dúvidas no seu código.

Teve que implementar o método paintComponent() na classe como protected e depois, cá, fora, como public?

Outra dúvida é o uso do ciclo for. É o primeiro exemplo que vejo com animação que precisa de um ciclo for para desenhar as formas geométricas.

Por vezes, quando executo, apenas mostra a janela e não acontece mais nada. Nem sequer cor de fundo é pintada. Este comportamento é aleatório. Parece bloquear ou ter um comportamento anómalo, não sei. Será por causa de recursos do CPU alocados pelo Thread lançado pela JVM ie (thread starvation)?

Valeu a ajuda! :smiley:

Ele deveria ser protected mesmo, afinal ele também é protected na superclasse.
Eu tornei public por engano.

Utilizei o for pois percebi que tu havias criado uma lista de Shape imagino que terás mais figuras para renderizar, certo? Neste caso basta adicionar estas figuras na sua lista.

Não consegui reproduzir esse problema.

Tente aplicar as seguintes alterações:

Classe DBPanel

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
class DBPanel extends JPanel {

    private int musicShapePosX = 85;
    private int musicShapePosY = 100;
    private int SEGMENT_SHAPE_LENGTH = 50;
    private int SHAPE_HEIGHT = 10;
    private float SHAPE_SPEED = 7.5f;

    private CustomShapeButton musicShapeButton;

    private List<CustomShapeButton> shapeButtons;

    public DBPanel() throws IOException {

        musicShapeButton = new CustomShapeButton(musicShapePosX, musicShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT);
        musicShapeButton.setBackground(Color.RED);
        musicShapeButton.setForeground(Color.ORANGE);

        shapeButtons = new ArrayList<>();
        shapeButtons.add(musicShapeButton);

        this.setOpaque(true);
        this.setFocusable(true);

        Thread thread = new Thread(() -> animate());
        thread.start();
    }

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

        Graphics2D g2 = (Graphics2D) g;

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2.setColor(getBackground().darker());
        g2.fillRect(0, 0, getWidth(), getHeight());

        for (CustomShapeButton shapeButton : shapeButtons) {
            shapeButton.paintComponent(g2);
        }
    }

    public void delay(int milliseconds) {
        try {
            Thread.sleep(milliseconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void animate() {
        while (true) {
            try {
                SwingUtilities.invokeAndWait(() -> {
                    animateButtonShapeMusic();
                    delay(50);
                    repaint();
                });
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    private void animateButtonShapeMusic() {
        if (musicShapePosY < 228) {
            musicShapePosY += SHAPE_SPEED;
            musicShapeButton.setShapePosition(musicShapePosX, musicShapePosY);
        }
    }
}

Classe CustomShapeButton:

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JButton;

@SuppressWarnings("serial")
public final class CustomShapeButton extends JButton implements Shape {

    private float segmentLength;
    private float shapeHeight;

    private GeneralPath path;

    public CustomShapeButton(float posX, float posY, float segmentLength, float shapeHeight) {
        this.segmentLength = segmentLength;
        this.shapeHeight = shapeHeight;
        setShapePosition(posX, posY);
    }

    public void setShapePosition(float posX, float posY) {
        float x0 = posX;
        float y0 = posY + shapeHeight;
        float x1 = posX - 3 + (segmentLength * 0.65f);
        float y1 = posY - segmentLength;
        float x2 = x1 - 6 + segmentLength * 1.35f;
        float y2 = y1;
        float x3 = x2 - 3 + (segmentLength * 0.65f);
        float y3 = y2 + shapeHeight + segmentLength;
        float x4 = x3 + 3 - (segmentLength * 0.65f);
        float y4 = y3 + shapeHeight + segmentLength;
        float x5 = x4 + 6 - (segmentLength * 1.35f);
        float y5 = y4;

        path = new GeneralPath();
        path.moveTo(x0, y0);
        path.lineTo(x1, y1);
        path.lineTo(x2, y2);
        path.lineTo(x3, y3);
        path.lineTo(x4, y4);
        path.lineTo(x5, y5);
        path.closePath();
    }

    @Override
    public Rectangle2D getBounds2D() {
        return path.getBounds2D();
    }

    @Override
    public boolean contains(double d, double d1) {
        return path.contains(d, d1);
    }

    @Override
    public boolean contains(Point2D pd) {
        return path.contains(pd);
    }

    @Override
    public boolean intersects(double d, double d1, double d2, double d3) {
        return path.intersects(d, d1, d2, d3);
    }

    @Override
    public boolean intersects(Rectangle2D rd) {
        return path.intersects(rd);
    }

    @Override
    public boolean contains(double d, double d1, double d2, double d3) {
        return path.contains(d, d1, d2, d3);
    }

    @Override
    public boolean contains(Rectangle2D rd) {
        return path.contains(rd);
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at) {
        return path.getPathIterator(at);
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at, double d) {
        return path.getPathIterator(at, d);
    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(getBackground());
        g2d.fill(path);
        g2d.setColor(getForeground());
        g2d.draw(path);
    }
}

Ok, parece ter melhorado a performance no resultado da animação.

Muito obrigado staroski, pelas dicas e disponibilidade na ajuda!

Abraço,
J Amorim

1 curtida

Olá de novo! desculpa incomodar mas só mais uma questão:

Estou a tentar adicionar imagens sobre cada shape e descomentei as seguintes linhas:

private final BufferedImage musicShapeImage; //Na classe DBPanel

musicShapeImage = ImageIO.read(getClass().getResource("/images/imageWoodMusic.png")); //No construtor da classe DBPanel()

g2.drawImage(musicShapeImage, musicImagePosX, musicImagePosY, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, (SEGMENT_SHAPE_LENGTH + SHAPE_HEIGHT) * 2, null); //No paintComponent da classe DBLauncher, logo a seguir ao ciclo for() onde está sendo desenhada cada shapeButton

Basta descomentar as 2 primeiras linhas para que nada seja desenhado no JPanel (nem sequer as shapes).

Tentei criar um array de imagens mas também não funcionou. Será necessário criar uma classe à parte exclusiva para as imagens?

Obrigado e desculpa o incómodo. J Amorim

Aqui você já está misturando as coisas.

Veja bem, se a imagem a ser desenhada pertence ao shape, porque você precisa desenhá-la na classe DBPanel?

Quem tem que ser responsável por desenhar/pintar a si mesmo é a própria classe CustomShapeButton.
Declare um atributo do tipo Image na classe CustomShapeButton.
Inicializa essa imagem no construtor, ou cria um setter pra ela.
No método paintComponent você desenha ela.

Observe a alteração no construtor da classeCustomShapeButton e no método paintComponent:

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JButton;

@SuppressWarnings("serial")
public final class CustomShapeButton extends JButton implements Shape {

    private float segmentLength;
    private float shapeHeight;

    private GeneralPath path;
    private Image image;

    public CustomShapeButton(float posX, float posY, float segmentLength, float shapeHeight) {
        this(posX, posY, segmentLength, shapeHeight, null);
    }

    public CustomShapeButton(float posX, float posY, float segmentLength, float shapeHeight, Image image) {
        this.segmentLength = segmentLength;
        this.shapeHeight = shapeHeight;
        this.image = image;
        setShapePosition(posX, posY);
    }

    public void setShapePosition(float posX, float posY) {
        float x0 = posX;
        float y0 = posY + shapeHeight;
        float x1 = posX - 3 + (segmentLength * 0.65f);
        float y1 = posY - segmentLength;
        float x2 = x1 - 6 + segmentLength * 1.35f;
        float y2 = y1;
        float x3 = x2 - 3 + (segmentLength * 0.65f);
        float y3 = y2 + shapeHeight + segmentLength;
        float x4 = x3 + 3 - (segmentLength * 0.65f);
        float y4 = y3 + shapeHeight + segmentLength;
        float x5 = x4 + 6 - (segmentLength * 1.35f);
        float y5 = y4;

        path = new GeneralPath();
        path.moveTo(x0, y0);
        path.lineTo(x1, y1);
        path.lineTo(x2, y2);
        path.lineTo(x3, y3);
        path.lineTo(x4, y4);
        path.lineTo(x5, y5);
        path.closePath();
    }

    @Override
    public Rectangle2D getBounds2D() {
        return path.getBounds2D();
    }

    @Override
    public boolean contains(double d, double d1) {
        return path.contains(d, d1);
    }

    @Override
    public boolean contains(Point2D pd) {
        return path.contains(pd);
    }

    @Override
    public boolean intersects(double d, double d1, double d2, double d3) {
        return path.intersects(d, d1, d2, d3);
    }

    @Override
    public boolean intersects(Rectangle2D rd) {
        return path.intersects(rd);
    }

    @Override
    public boolean contains(double d, double d1, double d2, double d3) {
        return path.contains(d, d1, d2, d3);
    }

    @Override
    public boolean contains(Rectangle2D rd) {
        return path.contains(rd);
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at) {
        return path.getPathIterator(at);
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at, double d) {
        return path.getPathIterator(at, d);
    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(getBackground());
        g2d.fill(path);
        g2d.setColor(getForeground());
        g2d.draw(path);

        if (image != null) {
            Rectangle bounds = getBounds2D().getBounds();
            g2d.drawImage(image, bounds.x, bounds.y, bounds.width, bounds.height, this);
        }
    }
}

Observe a alteração no construtor da classe DBPanel:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
class DBPanel extends JPanel {

    private int musicShapePosX = 85;
    private int musicShapePosY = 100;
    private int SEGMENT_SHAPE_LENGTH = 50;
    private int SHAPE_HEIGHT = 10;
    private float SHAPE_SPEED = 7.5f;

    private CustomShapeButton musicShapeButton;

    private List<CustomShapeButton> shapeButtons;

    public DBPanel() throws IOException {
        Image musicShapeImage = ImageIO.read(getClass().getResource("/images/imageWoodMusic.png"));
        musicShapeButton = new CustomShapeButton(musicShapePosX, musicShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT, musicShapeImage);
        musicShapeButton.setBackground(Color.RED);
        musicShapeButton.setForeground(Color.ORANGE);

        shapeButtons = new ArrayList<>();
        shapeButtons.add(musicShapeButton);

        this.setOpaque(true);
        this.setFocusable(true);

        Thread thread = new Thread(() -> animate());
        thread.start();
    }

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

        Graphics2D g2 = (Graphics2D) g;

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2.setColor(getBackground().darker());
        g2.fillRect(0, 0, getWidth(), getHeight());

        for (CustomShapeButton shapeButton : shapeButtons) {
            shapeButton.paintComponent(g2);
        }
    }

    public void delay(int milliseconds) {
        try {
            Thread.sleep(milliseconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void animate() {
        while (true) {
            try {
                SwingUtilities.invokeAndWait(() -> {
                    animateButtonShapeMusic();
                    delay(50);
                    repaint();
                });
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    private void animateButtonShapeMusic() {
        if (musicShapePosY < 228) {
            musicShapePosY += SHAPE_SPEED;
            musicShapeButton.setShapePosition(musicShapePosX, musicShapePosY);
        }
    }
}

Valeu mesmo! Entendi que estava errando quando estava a tentar que o DBPanel fosse responsável pelo desenho da imagem quando, na realidade e como você diz, faz sentido que seja a classe CustomShapeButton a desenhá-la!

Apliquei todas as alterações que você indicou mas, no entanto, agora não consigo visualizar nada no JPanel (ele aparece como janela, mas nada é desenhado nele, nem imagem, nem shapes).

Algum erro na minha DBLauncher() que constroi o JFrame e adiciona o JPanel através do getContentPane()?

Reenvio ambas as classes para você, com bastante código simplificado (porque, na realidade, esse projecto tem muitas shapes e imagens que vão ser adicionadas, mas enquanto a primera não estiver funcionando, não faz sentido implementar/visualizar o resto do código).

Repare que no meu código eu tenho implementado um mouseListener (mas que só atua quando se clica em cima da shape/imagem, para esta animar após esse clique).

Na realidade, espera-se que as shapes sejam desenhadas primeiro, depois serão desenhadas as imagens com a mesma forma das shapes (hexagono, como um favo de mel) em cima delas e, só no fim, é que se espera que o mouseListener entre em ação à espera que se clique no rato em cima de qualquer shape/imagem.

Cada shape que é desenhada por baixo de cada imagem funciona apenas como um esqueleto, que eu depois posso querer mover/brincar com ela no JPanel, sem mover a imagem mas movendo apenas a shape, por exemplo.

Eu já consegui fazer tudo isto antes do meu primeiro post aqui, apenas não estava conseguindo que as várias shapes fossem sendo apagadas, como já vimos antes e já foi resolvido.

Classes DBLauncher e DBPanel:

package dblauncher;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

@SuppressWarnings("ResultOfObjectAllocationIgnored")
public class DBLauncher 
{
    public static void main(String args[]) throws IOException 
    {
        new DBLauncher();
    }
    
    public DBLauncher() throws IOException
    {
        JFrame frame = new JFrame();
        
        frame.setSize(500, 500);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500, 500);
        frame.setLocation(430, 150);
        frame.setTitle("Database Launcher v1.0");
        frame.setResizable(false);
        frame.setVisible(true);
        
        DBPanel panel = new DBPanel();
        
        frame.getContentPane().add(panel, BorderLayout.CENTER);
    }
}

final class DBPanel extends JPanel implements MouseListener
{   
    private final int musicShapePosX = 85;
    private int musicShapePosY = 100;
    private final int SEGMENT_SHAPE_LENGTH = 50;
    private final int SHAPE_HEIGHT = 10;
    private final float SHAPE_SPEED = 7.5f; 

    private final CustomShapeButton musicShapeButton;    
    private final ArrayList<CustomShapeButton> shapeButtons;
    
    boolean musicButtonPressed = false;
    
    public DBPanel() throws IOException
    {     
        BufferedImage musicShapeImage = ImageIO.read(getClass().getResource("/images/imageWoodMusic.png"));
        
        shapeButtons = new ArrayList();
        
        musicShapeButton = new CustomShapeButton(musicShapePosX, musicShapePosY, SEGMENT_SHAPE_LENGTH, SHAPE_HEIGHT, musicShapeImage);
        
        musicShapeButton.setBackground(Color.LIGHT_GRAY);
        musicShapeButton.setForeground(Color.WHITE);
        shapeButtons.add(musicShapeButton);
              
        this.setOpaque(false);
        this.setFocusable(true);
        this.addMouseListener(this);
        
        Thread thread = new Thread(() -> animate());
        
        thread.start();
    }
    
    public void delay(int milliseconds)
    {
        try
        {
            Thread.sleep(milliseconds);
        } 
        catch (InterruptedException e)
        {
        }
    }
    
    @Override
    protected void paintComponent(Graphics g) 
    {
        super.paintComponent(g);
        
        Graphics2D g2 = (Graphics2D) g;
        
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  
        g2.setColor(getBackground().darker());
        g2.fillRect(0, 0, getWidth(), getHeight());
        
        for (CustomShapeButton shapeButton : shapeButtons)
        {
            shapeButton.paintComponent(g2);
        }
    }
     
    @SuppressWarnings({"SleepWhileInLoop", "empty-statement"})
    public void animate() 
    {
        while (true)
        {   
            if (musicButtonPressed == true)
            {
                animateButtonShapeMusic();
            }
        
            delay(35);
            repaint();
        }
    }

    @Override
    public void mouseClicked(MouseEvent me) 
    {   
        for (int i = 0; i < shapeButtons.size(); i++)
        {
            Shape shape = shapeButtons.get(i);
            
            if (shape.contains(me.getPoint()))
            {
                switch (i) 
                {
                    case 0:
                    {
                        musicButtonPressed = true;
                        System.out.println("Clicked on shape number " + i + " that represents the music button!");
                        
                        break;
                    }
         
					default: break;
                }
            }
        }
    }
    
   public void animateButtonShapeMusic()
    {
        if (musicShapePosY < 228)
        {
            musicShapePosY += SHAPE_SPEED;  
            musicShapeButton.setShapePosition(musicShapePosX, musicShapePosY);
        }
    }
    
    public void animateButtonShapeTv()
    {    
    }
    
    public void animateButtonShapeGames()
    {     
    }
    
    public void animateButtonShapeBooks()
    {     
    }
    
    public void animateButtonShapeTech()
    {    
    }

    @Override
    public void mousePressed(MouseEvent me) 
    { 
    }

    @Override
    public void mouseReleased(MouseEvent me) 
    {   
    }

    @Override
    public void mouseEntered(MouseEvent me) 
    {   
    }

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

Classe CustomShapeButton:

package dblauncher;

import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.JButton;

public final class CustomShapeButton extends JButton implements Shape
{   
    private float segmentLength;
    private float shapeHeight;
    
    private GeneralPath path;
    private BufferedImage image;

    public CustomShapeButton(float posX, float posY, float segmentLength, float shapeHeight)
    {    
        this(posX, posY, segmentLength, shapeHeight, null);
    }

    CustomShapeButton(float posX, float posY, float segmentLength, float shapeHeight, BufferedImage image) 
    {
        this.segmentLength = segmentLength;
        this.shapeHeight = shapeHeight;
        
        setShapePosition(posX, posY);
    }
    
    public void setShapePosition(float posX, float posY)
    {   
        float x0 = posX;
        float y0 = posY + shapeHeight;
        float x1 = posX - 3 + (segmentLength * 0.65f);
        float y1 = posY - segmentLength;
        float x2 = x1 - 6 + segmentLength * 1.35f;
        float y2 = y1;
        float x3 = x2 - 3 + (segmentLength * 0.65f);
        float y3 = y2 + shapeHeight + segmentLength;
        float x4 = x3 + 3 - (segmentLength * 0.65f);
        float y4 = y3 + shapeHeight + segmentLength;
        float x5 = x4 + 6 - (segmentLength * 1.35f);
        float y5 = y4;
        
        path = new GeneralPath();
        
        path.moveTo(x0, y0);
        path.lineTo(x1, y1);
        path.lineTo(x2, y2);
        path.lineTo(x3, y3);
        path.lineTo(x4, y4);
        path.lineTo(x5, y5);
        path.closePath();
    }

    @Override
    public Rectangle2D getBounds2D() 
    {
        return path.getBounds2D();
    }

    @Override
    public boolean contains(double d, double d1) 
    {
        return path.contains(d, d1);
    }

    @Override
    public boolean contains(Point2D pd) 
    {
        return path.contains(pd);
    }

    @Override
    public boolean intersects(double d, double d1, double d2, double d3) 
    {
        return path.intersects(d, d1, d2, d3);
    }

    @Override
    public boolean intersects(Rectangle2D rd) 
    {
        return path.intersects(rd);
    }

    @Override
    public boolean contains(double d, double d1, double d2, double d3) 
    {
        return path.contains(d, d1, d2, d3);
    }

    @Override
    public boolean contains(Rectangle2D rd) 
    {
        return path.contains(rd);
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at) 
    {
        return path.getPathIterator(at);
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at, double d) 
    {
        return path.getPathIterator(at, d);
    }
    
    public void translate(float distanceX, float distanceY) 
    {
        AffineTransform translation = new AffineTransform();
        
        translation.translate(distanceX, distanceY);
        path = (GeneralPath) path.createTransformedShape(translation);
    }
    
    @Override
    protected void paintComponent(Graphics g)
    {
        Graphics2D g2 = (Graphics2D) g;
        BasicStroke stroke = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
        
        g2.setStroke(stroke);
        g2.setColor(getBackground());
        g2.fill(path);
        g2.setColor(getForeground());
        g2.draw(path);
        
        if (image != null)
        {
            Rectangle bounds = getBounds2D().getBounds();
            
            g2.drawImage(image, bounds.x, bounds.y, bounds.width, bounds.height, this);
        }
    }
}

Obrigado e um abraço!
J Amorim

A imagem não era desenhada pois você esqueceu de inicializar ela no construtor do CustomShapeButton.

CustomShapeButton(float posX, float posY, float segmentLength, float shapeHeight, BufferedImage image) {
    this.segmentLength = segmentLength;
    this.shapeHeight = shapeHeight;
    this.image = image;  // você esqueceu dessa linha aqui
    setShapePosition(posX, posY);
}

O shape não era desenhado provavelmente por causa do layout do DBLauncher, reescrevi conforme abaixo e funcionou:

package dblauncher;

import java.awt.BorderLayout;
import java.awt.Container;
import java.io.IOException;

import javax.swing.JFrame;

@SuppressWarnings("ResultOfObjectAllocationIgnored")
public class DBLauncher extends JFrame { // estender JFrame

    public static void main(String args[]) throws IOException {
        DBLauncher dbLauncher = new DBLauncher();
        // não faz sentido chamar os dois métodos abaixo no construtor
        // o construtor serve para inicializar o objeto, não para executar ações
        dbLauncher.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        dbLauncher.setVisible(true);
    }

    public DBLauncher() throws IOException {
        super("Database Launcher v1.0");
        setSize(500, 500);
        setLocation(430, 150);
        setResizable(false);

        DBPanel panel = new DBPanel();

        Container container = getContentPane();
        container.setLayout(new BorderLayout()); // usar BorderLayout
        container.add(panel, BorderLayout.CENTER); // inserir o panel no centro do BorderLayout
    }
}

Bom, agora tá funcionando direitinho!! :smiley:

Você é barra nisso, cara. Obrigado pelas explicações, que serviram como chamada de atenção para futuras implementações.

Tou algo enferrujado em Java, mas como adoro isso nunca vou desistir, ehehe!!

Obrigado mais uma vez. :wink:

1 curtida