另一个是他们都没有用玻璃面板展示它
动画化a的不透明度状态glassPane与动画
化任何Swing组件的状态没有什么不同,毕竟,这glassPane只是另一个
组件。
一种是计时器系统不知道启动功能是否已启动,
并且使面板保持挂起状态,因为它在使面板褪色之前先关闭面板
,然后在显示面板之前先关闭面板,然后再不尝试再次关闭面板
这更多地是关于您自己的内部状态管理。面板
不在乎,它应该只是响应更改不透明度级别的请求,
向前或向后
您应该拥有的是一种“引擎”,可以
在达到某些状态时提供事件,这时您可以决定
应该做什么,并从“面板”本身中删除功能。
理论TL; DR
好吧,首先,一些理论。
动画…
动画是随着时间变化的幻觉。在您的情况下,您
要从0移到1,然后在指定的时间段内再次移回。这通常
称为“线性进行/动画”。大多数幼稚的动画实现
都将简单地向值添加一个常数增量,并一直这样做直到
达到期望的状态。这不是很幼稚,因为并非所有系统都是平等的。一些
将比其他一些能够更快地达到所需状态,从而使动画
不均匀并提供较差的用户体验。
相反,您应该专注于在固定时间段内执行操作,以
系统允许的最快速度计算所需值。这样
,动画就可以根据系统的
功能按要求“放下”帧。这通常称为“基于持续时间的动画”。
这种方法功能更强大,因为它允许您以
非常简单的方式来播放动画的速度。它还允许您执行一些非常
高级的操作,例如轻松操作,而
通过线性级数很难实现。
摇摆和动画…
秋千是单线程的。这意味着您不能
在事件调度线程的上下文中执行阻止或长时间运行的操作。
摆动也不是线程安全的。这意味着您不应
从EDT上下文外部更新UI(或UI依赖的任何状态)。
对于动画,您需要某种方式将事件快速,重复地发布
到EDT上,这将使您可以安全地对UI进行更改。为此,
最常用的工具是Swing Timer…
框架
因此,基于此,我们需要一种“引擎”,给定“范围”
和“持续时间”,可以定期通知“ ticks”的使用,从而
可以计算动画播放的进度,并根据
输入来计算我们应使用的值…简单…
我个人更喜欢使用动画库,但是
示例中提供的简单框架基本上将所有这些概念抽象为可重用的
框架。
这样吧…
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
…就像我说的,aglassPane只是另一个组成部分
这是一个使用框架的简单示例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:所需的类在前面的示例中



