Benjamin Keninger
Benjamin Keninger

Reputation: 31

Java swing Fade in/out Effect as a UI transition buffer?

I am a noob in java and am trying to make a kind of text adventure game. I want to be able to have the program have some kind of fade ability as it transitions from one layout of the UI to another.

I really have no idea what the best approach to this problem would be or if its really even feasible, but I have so far been trying to have a Jpanel that covers the entire window and uses a timer to fade in to cover everything else in black, or fades out from black to transparency thereby revealing everything underneath.

I have been testing this idea by trying to fade in/out the program at the start just to get the logic for the fade system working before trying to have it as a transition effect. The fade-out kind of works, but I have the program output the alpha level and the screen is turning black at around alpha 50 out of 255 which is confusing me. The fade-in does not work at all.

Here is the code for the fade method:

static int opacityCounter = 0;

public void fadeOut(JPanel frame){
    System.out.println(opacityCounter);
    opacityCounter = 0;
    fadeTimer = new Timer(50, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            frame.setBackground(new Color(0,0,0,opacityCounter));
            opacityCounter++;
            gui.window.add(frame);

            if(opacityCounter >= 255){
                opacityCounter = 255;
                fadeTimer.stop();
            }
            System.out.println(opacityCounter);
        }
    });
    fadeTimer.start();
}

This is the code where the "fadePanel" that covers the window is created and deployed in the method.

    fadeScreen = new JPanel();
    fadeScreen.setBounds(0,0,800,600);
    fadeScreen.setBackground(Color.black);
    window.add(fadeScreen);

    game.visibilityManager.fadeOut(this.fadeScreen);

To clarify I want something that goes from a UI layout like this:enter image description here

fades to black, before fading back to a UI that looks like this enter image description here

This is a minimal reproducible example:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Test {
JFrame window;
JPanel fadeScreen, screen1, screen2;
JLabel text1, text2;
Timer fadeTimer;

public Test(){

    //Frame Window
    window = new JFrame();
    window.setSize(800,600);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.getContentPane().setBackground(Color.blue);

    //Screen 1
    screen1 = new JPanel();
    screen1.setBounds(100, 100, 600, 125);
    screen1.setBackground(Color.white);
    text1 = new JLabel("Text1");
    screen1.add(text1);
    window.add(screen1);

    //Screen 2
    screen2 = new JPanel();
    screen2.setBounds(100, 400, 600, 125);
    screen2.setBackground(Color.white);
    text2 = new JLabel("Text2");
    screen2.add(text2);
    window.add(screen2);

    //Cover Panel
    fadeScreen = new JPanel();
    fadeScreen.setBounds(0,0,800,600);
    fadeScreen.setBackground(Color.black);
    window.add(fadeScreen);

    window.setVisible(true);


    //Comment out which method you don't want to use
    fadeOut(this.fadeScreen);
    //fadeIn(this.fadeScreen);
}


//Fade methods
int opacityCounter = 0;

public void fadeOut(JPanel frame){
    System.out.println(opacityCounter);
    opacityCounter = 0;
    fadeTimer = new Timer(50, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            frame.setBackground(new Color(0,0,0,opacityCounter));
            opacityCounter++;
            window.add(frame);

            if(opacityCounter >= 255){
                opacityCounter = 255;
                fadeTimer.stop();
            }
            System.out.println(opacityCounter);
        }
    });
    fadeTimer.start();
}

public void fadeIn(JPanel frame){
    System.out.println(opacityCounter);
    opacityCounter = 255;
    fadeTimer = new Timer(50, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            frame.setBackground(new Color(0,0,0,opacityCounter));
            opacityCounter--;
            window.add(frame);

            if(opacityCounter <= 0){
                opacityCounter = 0;
                fadeTimer.stop();
            }
            System.out.println(opacityCounter);
        }
    });
    fadeTimer.start();
}

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

Thanks in advance!

Upvotes: 0

Views: 1083

Answers (2)

Gilbert Le Blanc
Gilbert Le Blanc

Reputation: 51565

Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay close attention to the Concurrency in Swing and the Laying Out Components Within a Container sections.

This is Hovercraft Full Of Eels' answer. All I did was clean up the GUI creation and demonstrate how this would work with more than two JPanels.

I created five JPanels and displayed them in order with the fade-out/fade-in effect.

Here's the complete runnable code.

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class FadeEffectsTesting {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Fade Effects Testing");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            frame.add(new FadeEffectsTesting().getMainPanel());

            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        });
    }

    public static final String[] PANEL_SEQUENCE = { "Panel 1", "Panel 2", "Panel 3", "Panel 4",
            "Panel 5" };
    
    private int sequence = 0;

    private CardLayout cardLayout;
    
    private FadeAction action;

    private JPanel cardPanel, mainPanel;

    public FadeEffectsTesting() {
        this.mainPanel = new JPanel(new BorderLayout(5, 5));
        this.cardPanel = createCardPanel();
        this.action = new FadeAction(cardPanel);

        mainPanel.add(cardPanel, BorderLayout.CENTER);
        mainPanel.add(createButtonPanel(), BorderLayout.PAGE_END);
    }

    private JPanel createCardPanel() {
        cardLayout = new CardLayout();
        JPanel panel = new JPanel(cardLayout);

        panel.add(createTextPanel(Color.PINK, PANEL_SEQUENCE[0]), 
                PANEL_SEQUENCE[0]);
        panel.add(createTextPanel(new Color(131, 238, 255), 
                PANEL_SEQUENCE[1]), PANEL_SEQUENCE[1]);
        panel.add(createTextPanel(Color.PINK, PANEL_SEQUENCE[2]), 
                PANEL_SEQUENCE[2]);
        panel.add(createTextPanel(new Color(131, 238, 255), 
                PANEL_SEQUENCE[3]), PANEL_SEQUENCE[3]);
        panel.add(createTextPanel(Color.PINK, PANEL_SEQUENCE[4]), 
                PANEL_SEQUENCE[4]);

        return panel;
    }

    private JPanel createTextPanel(Color color, String text) {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBackground(color);
        int gap = 40;
        panel.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));

        JLabel label = new JLabel(text);
        label.setFont(label.getFont().deriveFont(Font.BOLD, 72f));
        panel.add(label);

        return panel;
    }

    private JPanel createButtonPanel() {
        JPanel panel = new JPanel(new FlowLayout());
        
        setFadeAction();
        JButton startFadeBtn = new JButton(action);
        panel.add(startFadeBtn);

        return panel;
    }
    
    public void setFadeAction() {
        action.setFromPanel(PANEL_SEQUENCE[sequence]);
        action.setToPanel(PANEL_SEQUENCE[sequence + 1]);
    }

    public JPanel getMainPanel() {
        return mainPanel;
    }

    public class FadeAction extends AbstractAction {
        private static final long serialVersionUID = 1L;

        private static final int FADE_DELAY = 20;
        private static final int UNFADE_VALUE = 255;

        private JPanel cardPanel;
        private JComponent glassPane;
        private JPanel coverPanel;
        private Timer fadeTimer;
        private int alphaValue;
        private boolean fadeOut;
        private String fromPanel, toPanel;

        public FadeAction(JPanel cardPanel) {
            super("Start Fade");
            this.putValue(MNEMONIC_KEY, KeyEvent.VK_S);
            this.cardPanel = cardPanel;
            this.alphaValue = 0;
            this.fadeOut = true;
        }

        public void setFromPanel(String fromPanel) {
            this.fromPanel = fromPanel;
        }

        public void setToPanel(String toPanel) {
            this.toPanel = toPanel;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            alphaValue = 0;
            fadeOut = true;
            setEnabled(false);

            CardLayout cl = (CardLayout) cardPanel.getLayout();
            cl.show(cardPanel, fromPanel);
            Window topLevelWindow = SwingUtilities.getWindowAncestor(cardPanel);
            glassPane = (JComponent) ((RootPaneContainer) topLevelWindow).getRootPane()
                    .getGlassPane();
            glassPane.setLayout(null);
            coverPanel = new JPanel();
            coverPanel.setSize(cardPanel.getSize());
            glassPane.add(coverPanel);
            glassPane.setVisible(true);

            fadeTimer = new Timer(FADE_DELAY, e2 -> fadeTimerActionPerformed(e2));
            fadeTimer.start();
        }

        private void fadeTimerActionPerformed(ActionEvent event) {
            coverPanel.setBackground(new Color(0, 0, 0, alphaValue));
            glassPane.repaint();

            if (fadeOut) {
                alphaValue += 3;
            } else if (alphaValue > 0) {
                alphaValue -= 3;
            } else {
                glassPane.remove(coverPanel);
                glassPane.setVisible(false);
                ((Timer) event.getSource()).stop();
                if (++sequence < (PANEL_SEQUENCE.length - 1)) {
                    setFadeAction();
                    setEnabled(true);
                }
            }

            if (alphaValue >= UNFADE_VALUE) {
                fadeOut = false;
                CardLayout cl = (CardLayout) cardPanel.getLayout();
                cl.show(cardPanel, toPanel);
            }
        }
        
    }

}

Upvotes: 0

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285460

I would try these things:

  • Use the GlassPane of the top-level window and make a section of it darker where you want to cover things up, using a Swing Timer.
  • Use a CardLayout to swap the underlying components, and make the swap when the covering JPanel is darkest.
  • Then undarken the covering panel after the swap.

For example (to write more code explanation later):

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class Test2 extends JPanel {
    public static final String PANEL_1 = "panel 1";
    public static final String PANEL_2 = "panel 2";
    private CardLayout cardLayout = new CardLayout();
    private JPanel cardPanel = new JPanel(cardLayout);
    private JPanel panel1 = new JPanel();
    private JPanel panel2 = new JPanel();
    private Action fadeAction = new FadeAction(cardPanel);
    
    public Test2() {
        JLabel label = new JLabel("Panel 1");
        label.setFont(label.getFont().deriveFont(Font.BOLD, 100f));
        panel1.setLayout(new GridBagLayout());
        int gap = 40;
        panel1.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
        panel1.add(label);
        panel1.setBackground(Color.PINK);
        
        label = new JLabel("Panel 2");
        label.setFont(label.getFont().deriveFont(Font.BOLD, 100f));
        panel2.setLayout(new GridBagLayout());
        panel2.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
        panel2.add(label);
        panel2.setBackground(new Color(131, 238, 255));
        
        
        cardPanel.add(panel1, PANEL_1);
        cardPanel.add(panel2, PANEL_2);
        
        JButton startFadeBtn = new JButton(fadeAction);
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(startFadeBtn);
        
        setLayout(new BorderLayout(5, 5));
        add(cardPanel, BorderLayout.CENTER);
        add(buttonPanel, BorderLayout.PAGE_END);
    }
    
    private static class FadeAction extends AbstractAction {
        private static final int FADE_DELAY = 20;
        private static final int UNFADE_VALUE = 255;
        private JPanel cardPanel;
        private JComponent glassPane;
        private JPanel coverPanel = new JPanel();
        private Timer fadeTimer;
        private int counter = 0;
        private boolean fade = true;

        public FadeAction(JPanel cardPanel) {
            super("Start Fade");
            putValue(MNEMONIC_KEY, KeyEvent.VK_S);
            this.cardPanel = cardPanel;
        }
        
        @Override
        public void actionPerformed(ActionEvent e) {
            counter = 0;
            fade = true;
            setEnabled(false);
            CardLayout cl = (CardLayout) cardPanel.getLayout();
            cl.show(cardPanel, PANEL_1);
            Window topLevelWindow = SwingUtilities.getWindowAncestor(cardPanel);
            glassPane = (JComponent) ((RootPaneContainer) topLevelWindow).getRootPane().getGlassPane();
            glassPane.setVisible(true);
            glassPane.setLayout(null);
            coverPanel.setSize(cardPanel.getSize());
            int x = cardPanel.getLocationOnScreen().x - glassPane.getLocationOnScreen().x;
            int y = cardPanel.getLocationOnScreen().y - glassPane.getLocationOnScreen().y;
            Point coverPanelPoint = new Point(x, y);
            coverPanel.setLocation(coverPanelPoint);
            glassPane.add(coverPanel);
            fadeTimer = new Timer(FADE_DELAY, e2 -> fadeTimerActionPerformed(e2));
            fadeTimer.start();
        }
        
        private void fadeTimerActionPerformed(ActionEvent e) {
            coverPanel.setBackground(new Color(0, 0, 0, counter));
            glassPane.repaint();
            if (fade) {
                counter++;
            } else if (counter > 0) {
                counter--;
            } else {
                glassPane.remove(coverPanel);
                glassPane.setVisible(false);
                setEnabled(true);
                ((Timer) e.getSource()).stop();
            }
            if (counter >= UNFADE_VALUE) {
                fade = false;
                CardLayout cl = (CardLayout) cardPanel.getLayout();
                cl.show(cardPanel, PANEL_2);
            }
        }
    }
    
    public static void main(String[] args) {        
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(new Test2());
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}

Upvotes: 1

Related Questions