如何通过命令JPanel及其组件和颜色淡入或淡入 [英] How can I fade out or fade in by command JPanel, its components and its color

查看:92
本文介绍了如何通过命令JPanel及其组件和颜色淡入或淡入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想制作一个包含带有白色背景,边框和味精请稍候"的JPanel的玻璃面板.

I wanted to make a Glass Panel that contain a JPanel with white background, border and the msg "please wait".

这是代码示例:

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

它会在等待查询时淡入和淡出. 问题是我得到了不好的结果.

It will fade in and out while waiting for the query. the problem is that I am getting a bad result.

需要帮助

推荐答案

另一个是它们都没有在玻璃面板上显示

the other is that none of them show it with glass panel

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

Animating the opacity state of a glassPane is no different from animating the state of any Swing component, after all, the glassPane is just another component.

一个是计时器系统不知道启动功能是否已启动,并且使面板保持挂起状态,因为它在使面板褪色之前先关闭面板,然后在其显示之前又不尝试再次关闭面板

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

This is more about your own internal state management. The panel shouldn't care, it should just be responding to the request to change opacity level, forward or backwards

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

What you should have, is some kind of "engine" which can provide events when certain states are achieved, at which time, you make decisions about what should be done, removing the functionality from the "panel" itself.

好的,首先,一些理论.

Okay, first, some theory.

动画是随着时间变化的幻觉.在您的情况下,您要从0移到1,然后在指定的时间段内再次移回.这通常称为线性进行/动画".大多数幼稚的动画实现都将简单地将常数delta添加到一个值,并一直这样做直到达到期望的状态.这是天真的,因为并非所有系统都是平等的.有些将比其他一些能够更快地达到所需状态,从而使动画不均匀并提供较差的用户体验.

Animation is the illusion of change over time. In your case, you're moving from 0 to 1 and back again over a specified period of time. This is commonly known as "linear progression/animation". Most naive animation implementations will simple add a constant delta to a value and keep doing so until a desired state is reached. This is naive because not all systems are equal. Some will be able to achieve the desired state faster than others, making the animation uneven and providing a poor user experience.

相反,您应该专注于在固定的时间段内执行操作,以系统允许的最快速度计算所需的值.这允许动画根据系统的功能按要求放下"帧.这通常称为基于持续时间的动画".

Instead, you should be focused on perform a operation over a fixed period of time, calculating the required value as fast as the system will allow. This allows the animation to "drop" frames as required based on the system's capabilities. This is commonly known as "duration based animation".

这种方法功能更强大,因为它允许您以非常简单的方式来播放动画的速度.它还允许您执行一些非常高级的操作,例如轻松操作,而通过线性级数很难实现.

This approach is much more powerful, as it allows you to play around with the speed of the animation in a very simply way. It also allows you do some very advanced operations, like easement, which wouldn't be easily achievable through a linear progression.

Swing是单线程的.这意味着您无法在事件调度线程的上下文中执行阻止或长时间运行的操作.

Swing is SINGLE threaded. This means you can't perform blocking or long running operations within the context of the Event Dispatching Thread.

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

Swing is also NOT thread safe. This means you shouldn't update the UI (or any state the UI depends on) from outside the context of the EDT.

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

For animation, what you need is some way to post, fast, repetitive, events onto the EDT, which will allow you to make changes to the UI safely. For this, the most common tool is a Swing Timer...

因此,基于此,我们需要一种引擎",给定范围"和持续时间"可以定期通知"ticks"的使用,由此我们可以计算出动画已经播放,并根据输入内容计算应该使用的值...简单...

So based on that, what we need is some kind of "engine", which given a "range" and a "duration" can notify use of "ticks" on a regular bases from which we can calculate the progression that the animation has played, and calculate the value we should use based on our inputs ... simple ...

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

I, personally, prefer to use an animation library, but the simple framework presented in the examples basically abstracts all these concepts into a re-usable framework.

nb:我没空了,所以基本示例中包含了底层框架

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

Okay, that's all nice and fluffy, but how does this actually help us. Essentially, the idea of the above is to abstract common functionality out and make it re-usable (and yes, I actually do use it, a lot)

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

What we now need, is a component which can actually use it, something like...

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属性的JPanel,该属性会更改组件的不透明度级别-基本上,这都是伪造的,因为Swing仅支持不透明和透明的组件,而不支持半透明的组件.因此,我们将组件设置为透明,然后自己手动绘制背景.

Okay, this is a pretty simple concept. It's a JPanel which has a alpha property which changes the opacity level of the component - basically, this is all faked, as Swing only support opaque and transparent components, not translucent components. So we set the component to be transparent and manually paint the background ourselves.

该组件公开了fadeInfadeOut两种方法,并支持FaderListener,可用于通知感兴趣的参与者淡入淡出操作已完成

The component exposes two methods, fadeIn and fadeOut and supports a FaderListener which can be used to notify interested parties that the fade operation has been completed

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只是另一个组件

... ok, as I said, a glassPane is just another component

这是一个简单的示例,它利用了框架的glassPane,并且当面板淡出时,会将glassPane重置为默认组件

This is a simple example which makes use of the frame's glassPane and will, when the panel is faded out, reset the glassPane to a default component

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:所需的类在前面的示例中

这篇关于如何通过命令JPanel及其组件和颜色淡入或淡入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆