如何通过命令 JPanel、其组件和颜色淡出或淡入
Posted
技术标签:
【中文标题】如何通过命令 JPanel、其组件和颜色淡出或淡入【英文标题】:How can I fade out or fade in by command JPanel, its components and its color 【发布时间】:2019-03-22 23:50:08 【问题描述】:我想制作一个玻璃面板,其中包含一个带有白色背景、边框和消息“请稍候”的 JPanel。
下面是代码示例:
JLabel glassLabel = new JLabel("Please wait");
FadingPanel msg = new FadingPanel();
glassLabel.setFont(new Font("Dialog", Font.BOLD, 26));
msg.setLayout(new BorderLayout());
msg.add(glassLabel,BorderLayout.NORTH);
msg.setBackground(Color.white);
msg.setFont(UIManager.getFont("Table.font").deriveFont(24f));
msg.setBorder(new CompoundBorder(new TitledBorder(""),
new EmptyBorder(20,20,20,20)));
等待查询时会淡入淡出。 问题是我得到了一个不好的结果。
需要帮助
【问题讨论】:
Java fade in and out of images; How to make 3 images fade in and fade out in JPanel?; Create a disappearing JPanel?; JProgressBar duplicating 我知道,并不是“所有”它们都在褪色面板,但老实说,基本概念是相同的。您需要一个 SwingTimer
并且您需要修改组件的不透明度级别 - 为此,您将不得不“伪造”它,因为 Swing 将组件视为不透明或透明,两者之间没有任何关系
所有这些例子都有两个问题,一个是 Timer 系统不知道 start 函数是否启动,它保持面板挂起,因为它在面板褪色之前关闭它,然后在之前它显示它,然后它不会尝试再次关闭它。另一个是他们都没有用玻璃面板显示它
首先,是什么让您认为在玻璃窗格上显示它与在其他任何地方显示它有什么不同。其次,您需要管理状态,这将假设您有一个“开始”值和一个“目标”。这意味着当目标 alpha 值为 0 并且Timer
达到零时,它可以“假设”面板可以被移除。就我个人而言,我会有一个通用的interface
,当Timer
“完成”设定的任务时会收到通知,它会决定应该做什么
JProgressBar duplicating 使用 glassPane
- 它只是反向执行
【参考方案1】:
另一个是他们都没有用玻璃面板显示它
动画glassPane
的不透明状态与动画任何 Swing 组件的状态没有什么不同,毕竟 glassPane
只是另一个组件。
一个是Timer系统不知道启动功能是否启动并且它保持面板挂起,因为它在面板褪色之前关闭它然后在它显示它之前然后它不会尝试再次关闭它
这更多是关于您自己的内部状态管理。面板不应该关心,它应该只是响应更改不透明度级别的请求,向前或向后
您应该拥有的是某种“引擎”,它可以在达到某些状态时提供事件,此时您可以决定应该做什么,从“面板”本身移除功能。
理论 TL;DR
好的,首先,一些理论。
动画...
动画是随时间变化的幻觉。在您的情况下,您在指定的时间段内从 0 移动到 1 并再次返回。这通常称为“线性进展/动画”。大多数幼稚的动画实现都会简单地将一个常量增量添加到一个值上,并一直这样做,直到达到所需的状态。这是幼稚的,因为并非所有系统都是平等的。有些人将能够比其他人更快地达到所需状态,从而使动画不均匀并提供较差的用户体验。
相反,您应该专注于在固定时间段内执行操作,在系统允许的范围内尽快计算所需的值。这允许动画根据系统的能力“丢弃”帧。这通常称为“基于持续时间的动画”。
这种方法更强大,因为它允许您以非常简单的方式来调整动画的速度。它还允许您执行一些非常高级的操作,例如地役权,这通过线性进程不容易实现。
摇摆和动画...
Swing 是单线程的。这意味着您不能在事件调度线程的上下文中执行阻塞或长时间运行的操作。
Swing 也不是线程安全的。这意味着您不应从 EDT 的上下文之外更新 UI(或 UI 所依赖的任何状态)。
对于动画,您需要通过某种方式将快速、重复的事件发布到 EDT,这样您就可以安全地更改 UI。为此,最常用的工具是 Swing Timer
...
框架
因此,基于此,我们需要某种“引擎”,它给定“范围”和“持续时间”,可以定期通知我们“滴答”,我们可以从中计算出进度动画已经播放,并根据我们的输入计算我们应该使用的值......简单......
我个人更喜欢使用动画库,但示例中提供的简单框架基本上将所有这些概念抽象为一个可重用的框架。
这样吧……
nb:我的空间用完了,所以底层框架包含在主示例中
好的,这一切都很好而且很蓬松,但这实际上对我们有什么帮助。本质上,上面的想法是抽象出通用功能并使其可重用(是的,我确实经常使用它)
我们现在需要的是一个可以实际使用它的组件,比如...
public interface FaderListener
public void fadeDidComplete(FadePane pane);
public class FadePane extends JPanel
private double alpha = 1;
private boolean fadingIn = true;
private DoubleAnimatable animatable;
private Duration duration = Duration.ofSeconds(5);
private List<FaderListener> listeners = new ArrayList<>(5);
public FadePane()
setOpaque(false);
public void addFadeListener(FaderListener listener)
listeners.add(listener);
public void removeFadeListener(FaderListener listener)
listeners.remove(listener);
public boolean isFadingIn()
return fadingIn;
public double getAlpha()
return alpha;
@Override
public void paint(Graphics g)
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive((float)getAlpha()));
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
super.paint(g2d);
g2d.dispose();
protected void fadeTo(double to)
double currentAlpha = getAlpha();
if (animatable != null)
animatable.stop();
animatable = null;
if (currentAlpha == to)
fadeDidComplete();
return;
DoubleRange animationRange = new DoubleRange(currentAlpha, to);
double maxFrom = to == 1 ? 1 : 0;
double maxTo = to == 1 ? 0 : 1;
DoubleRange maxRange = new DoubleRange(maxFrom, maxTo);
animatable = new DoubleAnimatable(animationRange, maxRange, duration, new AnimatableListener<Double>()
@Override
public void animationChanged(Animatable<Double> animatable)
alpha = animatable.getValue();
repaint();
, new AnimatableLifeCycleListenerAdapter<Double>()
@Override
public void animationCompleted(Animatable<Double> animatable)
fadeDidComplete();
);
Animator.INSTANCE.add(animatable);
public void fadeIn()
fadingIn = true;
fadeTo(1);
public void fadeOut()
fadingIn = false;
fadeTo(0);
protected void fadeDidComplete()
for (FaderListener listener : listeners)
listener.fadeDidComplete(this);
好的,这是一个非常简单的概念。这是一个JPanel
,它有一个alpha
属性,它改变了组件的不透明度级别——基本上,这都是伪造的,因为Swing 只支持不透明和透明组件,而不支持半透明组件。所以我们将组件设置为透明,并自己手动绘制背景。
该组件公开了两个方法,fadeIn
和fadeOut
,并支持FaderListener
,可用于通知相关方淡入淡出操作已完成
可运行示例...
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test
public static void main(String[] args)
new Test();
public Test()
EventQueue.invokeLater(new Runnable()
@Override
public void run()
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
);
public class TestPane extends JPanel
public TestPane()
setBackground(Color.RED);
setLayout(new BorderLayout());
FadePane pane = new FadePane();
pane.setLayout(new GridBagLayout());
pane.add(new JLabel("Look ma, no hands"));
add(pane);
JButton btn = new JButton("Switch");
btn.addActionListener(new ActionListener()
@Override
public void actionPerformed(ActionEvent e)
btn.setEnabled(false);
if (pane.isFadingIn())
pane.fadeOut();
else
pane.fadeIn();
);
add(btn, BorderLayout.SOUTH);
pane.addFadeListener(new FaderListener()
@Override
public void fadeDidComplete(FadePane pane)
btn.setEnabled(true);
);
@Override
public Dimension getPreferredSize()
return new Dimension(200, 200);
public interface FaderListener
public void fadeDidComplete(FadePane pane);
public class FadePane extends JPanel
private double alpha = 1;
private boolean fadingIn = true;
private DoubleAnimatable animatable;
private Duration duration = Duration.ofSeconds(5);
private List<FaderListener> listeners = new ArrayList<>(5);
public FadePane()
setOpaque(false);
public void addFadeListener(FaderListener listener)
listeners.add(listener);
public void removeFadeListener(FaderListener listener)
listeners.remove(listener);
public boolean isFadingIn()
return fadingIn;
public double getAlpha()
return alpha;
public void setFaddedOut()
alpha = 0;
fadingIn = false;
public void setFaddedIn()
alpha = 1;
fadingIn = true;
@Override
public void paint(Graphics g)
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive((float)getAlpha()));
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
super.paint(g2d);
g2d.dispose();
protected void fadeTo(double to)
double currentAlpha = getAlpha();
if (animatable != null)
animatable.stop();
animatable = null;
if (currentAlpha == to)
fadeDidComplete();
return;
DoubleRange animationRange = new DoubleRange(currentAlpha, to);
double maxFrom = to == 1 ? 1 : 0;
double maxTo = to == 1 ? 0 : 1;
DoubleRange maxRange = new DoubleRange(maxFrom, maxTo);
animatable = new DoubleAnimatable(animationRange, maxRange, duration, new AnimatableListener<Double>()
@Override
public void animationChanged(Animatable<Double> animatable)
alpha = animatable.getValue();
repaint();
, new AnimatableLifeCycleListenerAdapter<Double>()
@Override
public void animationCompleted(Animatable<Double> animatable)
fadeDidComplete();
);
Animator.INSTANCE.add(animatable);
public void fadeIn()
fadingIn = true;
fadeTo(1);
public void fadeOut()
fadingIn = false;
fadeTo(0);
protected void fadeDidComplete()
for (FaderListener listener : listeners)
listener.fadeDidComplete(this);
public class DoubleAnimatable extends AbstractAnimatable<Double>
public DoubleAnimatable(DoubleRange animationRange, DoubleRange maxRange, Duration duration, AnimatableListener<Double> listener, AnimatableLifeCycleListener<Double> lifeCycleListener)
super(animationRange, duration, listener, lifeCycleListener);
double maxDistance = maxRange.getDistance();
double aniDistance = animationRange.getDistance();
double progress = Math.min(100, Math.max(0, Math.abs(aniDistance / maxDistance)));
Duration remainingDuration = Duration.ofMillis((long) (duration.toMillis() * progress));
setDuration(remainingDuration);
public interface AnimatableListener<T>
public void animationChanged(Animatable<T> animatable);
public interface AnimatableLifeCycleListener<T>
public void animationStopped(Animatable<T> animatable);
public void animationCompleted(Animatable<T> animatable);
public void animationStarted(Animatable<T> animatable);
public void animationPaused(Animatable<T> animatable);
public class AnimatableLifeCycleListenerAdapter<T> implements AnimatableLifeCycleListener<T>
@Override
public void animationStopped(Animatable<T> animatable)
@Override
public void animationCompleted(Animatable<T> animatable)
@Override
public void animationStarted(Animatable<T> animatable)
@Override
public void animationPaused(Animatable<T> animatable)
public abstract class AbstractAnimatable<T> implements Animatable<T>
private Range<T> range;
private LocalDateTime startTime;
private Duration duration = Duration.ofSeconds(5);
private T value;
private AnimatableListener<T> animatableListener;
private AnimatableLifeCycleListener<T> lifeCycleListener;
// private Easement easement;
private double rawOffset;
public AbstractAnimatable(Range<T> range, Duration duration, AnimatableListener<T> listener)
this.range = range;
this.value = range.getFrom();
this.animatableListener = listener;
public AbstractAnimatable(Range<T> range, Duration duration, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener)
this(range, duration, listener);
this.lifeCycleListener = lifeCycleListener;
// public AbstractAnimatable(Range<T> range, Duration duration, Easement easement, AnimatableListener<T> listener)
// this(range, duration, listener);
// this.easement = easement;
//
//
// public AbstractAnimatable(Range<T> range, Duration duration, Easement easement, AnimatableListener<T> listener, AnimatableLifeCycleListener<T> lifeCycleListener)
// this(range, duration, easement, listener);
// this.lifeCycleListener = lifeCycleListener;
//
//
// public void setEasement(Easement easement)
// this.easement = easement;
//
//
// @Override
// public Easement getEasement()
// return easement;
//
public Duration getDuration()
return duration;
public Range<T> getRange()
return range;
public void setRange(Range<T> range)
this.range = range;
@Override
public T getValue()
return value;
protected void setDuration(Duration duration)
this.duration = duration;
public double getCurrentProgress(double rawProgress)
double progress = Math.min(1.0, Math.max(0.0, getRawProgress()));
// Easement easement = getEasement();
// if (easement != null)
// progress = easement.interpolate(progress);
//
return Math.min(1.0, Math.max(0.0, progress));
public double getRawProgress()
if (startTime == null)
return 0.0;
Duration duration = getDuration();
Duration runningTime = Duration.between(startTime, LocalDateTime.now());
double progress = rawOffset + (runningTime.toMillis() / (double) duration.toMillis());
return Math.min(1.0, Math.max(0.0, progress));
@Override
public void tick()
if (startTime == null)
startTime = LocalDateTime.now();
fireAnimationStarted();
double rawProgress = getRawProgress();
double progress = getCurrentProgress(rawProgress);
if (rawProgress >= 1.0)
progress = 1.0;
value = getRange().valueAt(progress);
fireAnimationChanged();
if (rawProgress >= 1.0)
fireAnimationCompleted();
@Override
public void start()
if (startTime != null)
// Restart?
return;
Animator.INSTANCE.add(this);
@Override
public void stop()
stopWithNotification(true);
@Override
public void pause()
rawOffset += getRawProgress();
stopWithNotification(false);
double remainingProgress = 1.0 - rawOffset;
Duration remainingTime = getDuration().minusMillis((long) remainingProgress);
setDuration(remainingTime);
lifeCycleListener.animationStopped(this);
protected void fireAnimationChanged()
if (animatableListener == null)
return;
animatableListener.animationChanged(this);
protected void fireAnimationCompleted()
stopWithNotification(false);
if (lifeCycleListener == null)
return;
lifeCycleListener.animationCompleted(this);
protected void fireAnimationStarted()
if (lifeCycleListener == null)
return;
lifeCycleListener.animationStarted(this);
protected void fireAnimationPaused()
if (lifeCycleListener == null)
return;
lifeCycleListener.animationPaused(this);
protected void stopWithNotification(boolean notify)
Animator.INSTANCE.remove(this);
startTime = null;
if (notify)
if (lifeCycleListener == null)
return;
lifeCycleListener.animationStopped(this);
public interface Animatable<T>
public Range<T> getRange();
public T getValue();
public void tick();
public Duration getDuration();
//public Easement getEasement();
// Wondering if these should be part of a secondary interface
// Provide a "self managed" unit of work
public void start();
public void stop();
public void pause();
public abstract class Range<T>
private T from;
private T to;
public Range(T from, T to)
this.from = from;
this.to = to;
public T getFrom()
return from;
public T getTo()
return to;
@Override
public String toString()
return "From " + getFrom() + " to " + getTo();
public abstract T valueAt(double progress);
public class DoubleRange extends Range<Double>
public DoubleRange(Double from, Double to)
super(from, to);
public Double getDistance()
return getTo() - getFrom();
@Override
public Double valueAt(double progress)
double distance = getDistance();
double value = distance * progress;
value += getFrom();
return value;
public enum Animator
INSTANCE;
private Timer timer;
private List<Animatable> properies;
private Animator()
properies = new ArrayList<>(5);
timer = new Timer(5, new ActionListener()
@Override
public void actionPerformed(ActionEvent e)
List<Animatable> copy = new ArrayList<>(properies);
Iterator<Animatable> it = copy.iterator();
while (it.hasNext())
Animatable ap = it.next();
ap.tick();
if (properies.isEmpty())
timer.stop();
);
public void add(Animatable ap)
properies.add(ap);
timer.start();
protected void removeAll(List<Animatable> completed)
properies.removeAll(completed);
public void remove(Animatable ap)
properies.remove(ap);
if (properies.isEmpty())
timer.stop();
但这不是
glassPane
...好吧,正如我所说,glassPane
只是另一个组件
这是一个使用框架的glassPane
的简单示例,当面板淡出时,将glassPane
重置为默认组件
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Test
public static void main(String[] args)
new Test();
public Test()
EventQueue.invokeLater(new Runnable()
@Override
public void run()
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
);
public class TestPane extends JPanel
public TestPane()
setLayout(new GridBagLayout());
JButton btn = new JButton("Switch");
btn.addActionListener(new ActionListener()
@Override
public void actionPerformed(ActionEvent e)
Window window = SwingUtilities.getWindowAncestor(TestPane.this);
if (!(window instanceof JFrame))
System.out.println("Not out frame");
return;
JFrame frame = (JFrame) window;
FadePane pane = new FadePane();
pane.setLayout(new BorderLayout());
pane.add(new JLabel("All your base are belong to us"));
pane.setFaddedOut();
pane.addFadeListener(new FaderListener()
@Override
public void fadeDidComplete(FadePane pane)
System.out.println("Completed");
if (pane.getAlpha() == 1)
System.out.println("Fade out");
pane.fadeOut();
else
System.out.println("Remove glasspane");
frame.setGlassPane(new JPanel());
);
frame.setGlassPane(pane);
System.out.println("Fade in");
pane.setVisible(true);
pane.fadeIn();
);
add(btn);
@Override
public Dimension getPreferredSize()
return new Dimension(200, 200);
nb:所需的类在前面的例子中
【讨论】:
是的!!!!!!这就是我要找的!!!!谢谢疯狂程序员【参考方案2】:考虑使用JDialog 容器。当它是undecorated时,你可以改变它的opacity:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.WindowConstants;
public class FadeDialog extends JDialog
private float alfa = 1;
private JLabel label;
private boolean isFadeIn = true;
private JButton fadeIn, fadeOut;
FadeDialog()
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setLocation(new Point(300, 300));
getContentPane().setLayout(new BorderLayout(5,0));
setUndecorated(true); //opacity supported for undecorated JDialogs
JButton close = new JButton("Close");
close.addActionListener(e -> dispose());
getContentPane().add(close, BorderLayout.PAGE_END);
getContentPane().add(new ContentPane(), BorderLayout.CENTER);
pack();
setVisible(true);
Timer timer = new Timer(2000, e -> fade());//endless fade-in-out loop
timer.setInitialDelay(100);
timer.start();
void fade()
alfa = isFadeIn ? alfa + 0.1f : alfa -0.1f;
if(alfa <=0 )
alfa = 0; isFadeIn = true;
else if(alfa >= 1)
alfa = 1; isFadeIn = false;
fadeIn.setEnabled(! isFadeIn); fadeOut.setEnabled(isFadeIn);
label.setText("Alfa is " + alfa);
setOpacity(alfa); //set JDialog opacity
class ContentPane extends JPanel
ContentPane()
setPreferredSize(new Dimension(200, 100));
setLayout(new BorderLayout());
fadeIn = new JButton("Fade In");
fadeIn.addActionListener(e -> isFadeIn = true);
add(fadeIn, BorderLayout.PAGE_START);
label = new JLabel("Alfa is " + alfa);
add(label, BorderLayout.CENTER);
fadeOut = new JButton("Fade Out");
fadeOut.addActionListener(e -> isFadeIn = false);
add(fadeOut, BorderLayout.PAGE_END);
public static void main(String[] args)
new FadeDialog();
【讨论】:
以上是关于如何通过命令 JPanel、其组件和颜色淡出或淡入的主要内容,如果未能解决你的问题,请参考以下文章