繁体   English   中英

如何通过命令 JPanel、其组件及其颜色淡出或淡入

[英]How can I fade out or fade in by command JPanel, its components and its color

我想制作一个玻璃面板,其中包含一个带有白色背景、边框和味精“请稍候”的 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)));

它会在等待查询时淡入淡出。 问题是我得到了一个糟糕的结果。

需要帮忙

另一个是他们都没有用玻璃面板显示它

glassPane的不透明状态设置动画与为任何 Swing 组件的状态设置动画没有什么不同,毕竟glassPane只是另一个组件。

一个是定时器系统不知道启动功能是否启动,它使面板保持挂起,因为它在面板消失之前关闭它,然后在它显示之前关闭它,然后它不会再次尝试关闭它

这更多是关于您自己的内部状态管理。 面板不应该关心,它应该只是响应更改不透明度级别的请求,向前或向后

您应该拥有的是某种“引擎”,它可以在达到某些状态时提供事件,此时,您可以决定应该做什么,从“面板”本身中删除功能。

理论 TL; DR

好的,首先,一些理论。

动画片...

动画是随时间变化的幻觉。 在您的情况下,您在指定的时间段内从 0 移动到 1 并再次返回。 这通常称为“线性进程/动画”。 大多数简单的动画实现将简单地向一个值添加一个恒定的增量,并继续这样做直到达到所需的状态。 这是天真的,因为并非所有系统都是平等的。 有些将能够比其他更快地达到所需的状态,从而使动画不均匀并提供糟糕的用户体验。

相反,您应该专注于在固定时间段内执行操作,以系统允许的速度计算所需的值。 这允许动画根据系统功能根据需要“丢弃”帧。 这通常称为“基于持续时间的动画”。

这种方法更强大,因为它允许您以非常简单的方式玩弄动画的速度。 它还允许您执行一些非常高级的操作,例如地役权,这是通过线性进程不容易实现的。

摇摆和动画...

Swing 是单线程的。 这意味着您不能在事件调度线程的上下文中执行阻塞或长时间运行的操作。

Swing 也不是线程安全的。 这意味着您不应从 EDT 的上下文之外更新 UI(或 UI 所依赖的任何状态)。

对于动画,您需要某种方式将快速、重复的事件发布到 EDT 上,这将允许您安全地更改 UI。 为此,最常用的工具是 Swing Timer ...

框架

所以基于此,我们需要的是某种“引擎”,它给定“范围”和“持续时间”可以定期通知我们“滴答”,我们可以从中计算动画播放的进度,并根据我们的输入计算我们应该使用的值......简单......

我个人更喜欢使用动画库,但是示例中提供的简单框架基本上将所有这些概念抽象为一个可重用的框架。

弄成这样...

注意:我用完了空间,所以底层框架包含在主要示例中

好的,这一切都很好而且很蓬松,但这对我们有什么帮助。 本质上,上面的想法是将通用功能抽象出来并使其可重用(是的,我确实经常使用它)

我们现在需要的是一个可以实际使用它的组件,例如......

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 只支持不透明和透明组件,不支持半透明组件。 所以我们将组件设置为透明并自己手动绘制背景。

该组件公开了两个方法, fadeInfadeOut并支持一个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);
        }

    }

}

注意:所需的类在前面的例子中

考虑使用JDialog容器。 当它未装饰时,您可以更改其不透明度

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();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM