Search code examples
javauser-interfacejbuttonartifactsvisual-glitch

Java Drawing Button Animations In Top Corner


I have a JFrame with a border layout that has two JPanels. One JPanel in the southern part of the frame has some JButtons, (an n number), and one displays a connect - 4 board (this is for a CS class). For some strange reason I am having a wierd bug where the animaitions for buttons are being displayed in the top left hand corner.

enter image description here

Its quite a strange bug, Im not expert on swing I have wondered if it has anything to do with me running the paint method ([with repaint() which I presume is on another thread) in one JPanel and actionPerformed in another JPanel (which I presume is on another thread) and there is some conflict or disordering in the rendering. Just a guess.

Here is some of the GUI code:

class Connect4GUI extends JFrame implements SetSpotInterface
{
    private Connect4 connect4;
    private Connect4PieceDisplay pieceDisplay;
    public static final String DEFAULT_TITLE = "Connect 4";
    //...
    public Connect4GUI( Connect4 connect4_, String title )
    {
        setTitle( title );
        setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        connect4 = connect4_;
        pieceDisplay = new Connect4PieceDisplay( this );
        add( new Connect4ButtonDispencer( this ), BorderLayout.SOUTH );
        add( new Connect4PieceDisplay( this ), BorderLayout.CENTER );
        pack();
        setVisible( true );
    }
    public Connect4GUI( Connect4 connect4_ ) {
        this( connect4_, DEFAULT_TITLE );
    }
    public Connect4GUI() {
        this( new Connect4() );
    }
    //...
}

abstract class Connect4PanelComponent extends JPanel
{
    public static final Connect4GUI DEFAULT_DISPLAY_WINDOW = null;
    public final static Color DEFAULT_BACKROUND_COLOR = Color.YELLOW;
    Connect4GUI displayGUI;
    public Color backroundColor;
}

class Connect4PieceDisplay extends Connect4PanelComponent
{
    XY offsetScaler, offsetModifier, pixelOffset, spacingModifier, spacingPixelOffset;
    public final static XY DEFAULT_OFFSET_SCALER = new XY( 5, 5 );
    public final static XY DEFAULT_OFFSET_MODIFIER = new XY( 1, 1 );
    public final static XY DEFAULT_PIXEL_OFFSET = new XY( 6, 6 );
    public final static XY DEFAULT_SPACING_MODIFIER = new XY( 2, 2 );
    public final static XY DEFAULT_SPACING_PIXEL_OFFSET = new XY( 0, 0 );
    public Connect4PieceDisplay( Connect4GUI displayGUI_, XY offsetScaler_, XY offsetModifier_, XY pixelOffset_, XY spacingModifier_, XY spacingPixelOffset_, Color backroundColor_ )
    {
        super();
        displayGUI = displayGUI_;
        offsetScaler = offsetScaler_;
        offsetModifier = offsetModifier_;
        pixelOffset = pixelOffset_;
        spacingModifier = spacingModifier_;
        spacingPixelOffset = spacingPixelOffset_;
        backroundColor = backroundColor_;

        setOpaque( true );
        setBackground( backroundColor );
    }
    public Connect4PieceDisplay( Connect4GUI displayGUI_, XY offsetScaler_, XY offsetModifier_, XY pixelOffset_, XY spacingModifier_, XY spacingPixelOffset_ ) {
        this( displayGUI_, offsetScaler_, offsetModifier_, pixelOffset_, spacingModifier_, spacingPixelOffset_, DEFAULT_BACKROUND_COLOR );
    }
    public Connect4PieceDisplay( Connect4GUI displayGUI_, XY offsetScaler_, XY offsetModifier_, XY pixelOffset_, XY spacingModifier_ ) {
        this( displayGUI_, offsetScaler_, offsetModifier_, pixelOffset_, spacingModifier_, new XY( DEFAULT_SPACING_PIXEL_OFFSET ) );
    }
    public Connect4PieceDisplay( Connect4GUI displayGUI_, XY offsetScaler_, XY offsetModifier_, XY pixelOffset_ ) {
        this( displayGUI_, offsetScaler_, offsetModifier_, pixelOffset_, new XY( DEFAULT_SPACING_MODIFIER ) );
    }
    public Connect4PieceDisplay( Connect4GUI displayGUI_, XY offsetScaler_, XY offsetModifier_ ) {
        this( displayGUI_, offsetScaler_, offsetModifier_, new XY( DEFAULT_PIXEL_OFFSET ) );
    }
    public Connect4PieceDisplay( Connect4GUI displayGUI_, XY offsetScaler_ ) {
        this( displayGUI_, offsetScaler_, new XY( DEFAULT_OFFSET_MODIFIER ) );
    }
    public Connect4PieceDisplay( Connect4GUI displayGUI_ ) {
        this( displayGUI_, new XY( DEFAULT_OFFSET_SCALER ) );
    }
    public Connect4PieceDisplay() {
        this( DEFAULT_DISPLAY_WINDOW );
    }
    public void DefaultSetUpModifiers( int offset, int offsetModifier_, int spacing, int spacingPixel )
    {
        offsetScaler = new XY( offset, offset );
        offsetModifier = new XY( offsetModifier_, offsetModifier_ );
        pixelOffset = new XY( ( offset + 1 ), ( offset + 1 ) );
        spacingModifier = new XY( spacing, spacing );
        spacingPixelOffset = new XY( spacingPixel, spacingPixel );
    }
    public void paint( Graphics graphics )
    {
        final int X_DIMENTION = displayGUI.GetConnect4().dimentions.x;
        final int Y_DIMENTION = displayGUI.GetConnect4().dimentions.y;
        final int X_SPACING = ( getWidth() / X_DIMENTION );
        final int Y_SPACING = ( getHeight() / Y_DIMENTION );
        final int AMOUNT_OF_COLORS_IN_COLOR_MAP = displayGUI.colorMap.size();
        for( int i = 0; i < X_DIMENTION; ++i )
        {
            for( int j = 0; j < Y_DIMENTION; ++j )
            {
                final char CURRENT_SYMBOL = displayGUI.GetConnect4().GetSpot( i, j );
                for( int o = 0; o < AMOUNT_OF_COLORS_IN_COLOR_MAP; ++o )
                {
                    if( CURRENT_SYMBOL == displayGUI.colorMap.get( o ).GetSymbol() ) {
                        graphics.setColor( displayGUI.colorMap.get( o ).GetColor() );
                        break;
                    }
                }
                graphics.fillOval( AttainXOffset( i ), AttainYOffset( j ), AttainXSpacing(), AttainYSpacing() );
            }
        }
        repaint();
    }
    public int AttainXOffset( int x ) {
        final int X_SPACING = ( getWidth() / displayGUI.GetConnect4().dimentions.x );
        return ( ( X_SPACING / DEFAULT_OFFSET_SCALER.x ) + DEFAULT_PIXEL_OFFSET.x + ( X_SPACING * DEFAULT_OFFSET_MODIFIER.x * x ) );
    }
    public int AttainYOffset( int y ) {
        final int Y_SPACING = ( getHeight() / displayGUI.GetConnect4().dimentions.y );
        return ( ( Y_SPACING / DEFAULT_OFFSET_SCALER.y ) + DEFAULT_PIXEL_OFFSET.y + ( Y_SPACING * DEFAULT_OFFSET_MODIFIER.y * y ) );
    }
    public int AttainXSpacing() {
        final int X_SPACING = ( getWidth() / displayGUI.GetConnect4().dimentions.x );
        return ( ( X_SPACING / DEFAULT_SPACING_MODIFIER.x ) + DEFAULT_SPACING_PIXEL_OFFSET.x );
    }
    public int AttainYSpacing() {
        final int Y_SPACING = ( getHeight() / displayGUI.GetConnect4().dimentions.y );
        return ( ( Y_SPACING / DEFAULT_SPACING_MODIFIER.y ) + DEFAULT_SPACING_PIXEL_OFFSET.y );
    }
}

class Connect4ButtonDispencer extends Connect4PanelComponent implements ActionListener
{
    public final static String DEFAULT_BUTTON_MESSAGE = "";
    public final static int DEPTH_OF_BUTTON_GRID = 1;
    public JButton[] dispencerButtons;
    public String buttonMessage;
    public Connect4ButtonDispencer( Connect4GUI displayGUI_, Color backroundColor_, int amountOfDispencerButtons_, String buttonMessage_ )
    {
        super();
        displayGUI = displayGUI_;
        backroundColor = backroundColor_;
        buttonMessage = buttonMessage_;
        setLayout( new GridLayout( DEPTH_OF_BUTTON_GRID, amountOfDispencerButtons_ ) );
        dispencerButtons = DefaultSetUpDispencerButtons( amountOfDispencerButtons_ );
        buttonMessage = buttonMessage_;
        setOpaque( true );
        setBackground( backroundColor );
    }
    public Connect4ButtonDispencer( Connect4GUI displayGUI_, Color backroundColor_, int amountOfDispencerButtons_ ) {
        this( displayGUI_, backroundColor_, amountOfDispencerButtons_, DEFAULT_BUTTON_MESSAGE );
    }
    public Connect4ButtonDispencer( Connect4GUI displayGUI_, Color backroundColor_ ) {
        this( displayGUI_, backroundColor_, displayGUI_.GetConnect4().dimentions.x );
    }
    public Connect4ButtonDispencer( Connect4GUI displayGUI_ ) {
        this( displayGUI_, DEFAULT_BACKROUND_COLOR );
    }
    public Connect4ButtonDispencer() {
        this( DEFAULT_DISPLAY_WINDOW );
    }
    public JButton[] DefaultSetUpDispencerButtons( int length )
    {
        dispencerButtons = new JButton[ length ];
        //What if I change something in between when I declare the array and the 'for' loop?//
        final int AMOUNT_OF_DISPENCER_BUTTONS = dispencerButtons.length;
        for( int i = 0; i < AMOUNT_OF_DISPENCER_BUTTONS; ++i )
        {
            dispencerButtons[ i ] = new JButton( ( buttonMessage + ( i + 1 ) ) );
            dispencerButtons[ i ].addActionListener( this );
            add( dispencerButtons[ i ] );
        }
        return dispencerButtons;
    }
    public void actionPerformed( ActionEvent event )
    {
        final int AMOUNT_OF_DISPENCER_BUTTONS = dispencerButtons.length;
        for( int i = 0; i < AMOUNT_OF_DISPENCER_BUTTONS; ++i )
        {
            System.out.println( "A" );
            if( dispencerButtons[ i ] == event.getSource() )
            {
                System.out.println( "B" );
                //...it could change dynamically especially when this is on a thread. #parinoi .//
                final int Y_DIMENTIONS = displayGUI.GetConnect4().dimentions.y;
                for( int j = ( Y_DIMENTIONS - 1 ); j >= 0; --j )
                {
                    if( displayGUI.GetConnect4().GetSpot( i, j ) == Connect4.EMPTY_SPOT )
                    {
                        displayGUI.GetConnect4().SetSpot( i, j, displayGUI.turnSymbol );
                        displayGUI.ChangeTurn();
                        break;
                    }
                }
                break;
            }
        }
    }
}

Any insight or help on this issue is greatly appreciated! :)

Thank you - TFB :-)


Solution

    1. Don't call repaint() from within paint(...) as this leads to
      • uncontrollable animation, and
      • using a painting method for something for which it was not intended.
    2. You shouldn't even be overriding paint but rather should be overriding paintComponent.
    3. And here's the big issue: you must call the super's painting method within your painting override. So if you're overriding paintComponent, call the super.paintComponent(...) method within it.