【问题标题】:Fire stateChanged event on JTabbedPane在 JTabbedPane 上触发 stateChanged 事件
【发布时间】:2013-08-01 11:14:26
【问题描述】:

我有带有淡入淡出动画的 JTabbedPane,这是在用户单击选项卡时执行的。为了处理动画,我重写了stateChanged 方法。

public class AnimatedJTabbedPane extends JTabbedPane implements ChangeListener, TimingTarget{

/**
 * 
 */
private static final long serialVersionUID = 1L;
protected BufferedImage previousTabImage;
protected BufferedImage bufferImage;
protected BufferedImage nextTabImage;
protected boolean animationStarted = false;
protected boolean paintPreviousImage = false;
protected boolean leftToRightIndex = false;
protected float fraction = 0.0f;
protected Animator animator;
protected int previousTabIndex = -1;

public AnimatedJTabbedPane(int tabPlacement) {
    super(tabPlacement);

    this.animator = new Animator(300, this);
    this.animator.setAcceleration(0.1f);
    this.animator.setDeceleration(0.8f);
    this.addChangeListener(this);
}

public void stateChanged(ChangeEvent e) {

    if(this.previousTabIndex != this.getSelectedIndex()){
        if(this.previousTabIndex == -1){
            this.previousTabIndex = this.getSelectedIndex();
        }
        else{
            this.paintPreviousImage = true; 
            boolean interrupted = false;
            if(this.animator.isRunning()){
                this.animator.stop();
                interrupted= true;
            }

            Component previousComponent = this.getComponentAt(this.previousTabIndex);

            this.previousTabImage = this.getGraphicsConfiguration().createCompatibleImage(
                    previousComponent.getWidth(), previousComponent.getHeight(), Transparency.TRANSLUCENT);

            previousComponent.paint(this.previousTabImage.getGraphics());

            Component nextComponent = this.getComponentAt(this.getSelectedIndex());
            this.nextTabImage = this.getGraphicsConfiguration().createCompatibleImage(
                    previousComponent.getWidth(), previousComponent.getHeight(), Transparency.TRANSLUCENT);

            nextComponent.paint(this.nextTabImage.getGraphics());

            if(this.previousTabIndex < this.getSelectedIndex()){
                this.leftToRightIndex = true;
            }
            else{
                this.leftToRightIndex = false;
            }

            this.previousTabIndex = this.getSelectedIndex();
            if(interrupted){
                this.animator.setStartFraction(1-this.fraction);
            }
            else{
                this.animator.setStartFraction(0);
            }
            this.animationStarted = true;
            this.animator.start();
        }
    }
}


@Override
public void paintChildren(Graphics g){
    if(this.getComponentCount() != 0){
        Rectangle childrenPosition = this.getComponentAt(0).getBounds();

        if(this.bufferImage == null || 
                this.bufferImage.getWidth() != this.getWidth() ||
                this.bufferImage.getHeight() != this.getHeight()){
            this.bufferImage = new BufferedImage(
                    this.getWidth(), 
                    this.getHeight(), 
                    BufferedImage.TYPE_INT_ARGB);
        }

        if(this.animationStarted){
            if(this.paintPreviousImage){
                g.drawImage(this.bufferImage, 0, 0, null);
                this.paintPreviousImage = false;
            }
            else{
                Graphics2D g2d = (Graphics2D)this.bufferImage.createGraphics(); 
                int x = (int)childrenPosition.getX();
                int y = (int)childrenPosition.getY();

                this.performFadeAnimation(g2d, x, y);

                g.drawImage(this.bufferImage, 0, 0, null);
                g2d.dispose();
            }
            g.dispose();
        }
        else{
            Graphics2D g2d = (Graphics2D)this.bufferImage.createGraphics(); 

            super.paintChildren(g2d);
            g.drawImage(this.bufferImage, 0, 0, null);
            g2d.dispose();
            g.dispose();
        }
    }
    else{
        super.paintChildren(g);
    }
}


protected void performFadeAnimation(Graphics2D g2d, final double x, final double y){
    g2d.drawImage(this.previousTabImage, (int)x, (int)y, null);

    AlphaComposite composite = 
            AlphaComposite.getInstance(AlphaComposite.SRC_OVER, this.fraction);
    g2d.setComposite(composite);

    g2d.drawImage(this.nextTabImage,  (int)x, (int)y, null);
}

@Override
public void begin() {
    // TODO Auto-generated method stub
}

@Override
public void end() {
    this.animationStarted = false;
    this.repaint();
    this.nextTabImage.flush();
    this.nextTabImage = null;
    this.previousTabImage.flush();
    this.previousTabImage = null;
}

@Override
public void repeat() {
    // TODO Auto-generated method stub
}

@Override
public void timingEvent(float fraction) {
    this.fraction = fraction;
    this.repaint();
}

}

当用户手动单击选项卡时,这非常有用。 stateChanged 事件被调用,标签随着动画的变化而变化。

我需要从代码中更改选定的选项卡,所以我使用setSelectedIndex(int) 方法。

public void setSelectedTab(int tab){
    animatedTabbedPane.setSelectedIndex(tab);
}

但现在标签在setSelectedIndex 结束后立即更改 - 没有动画,然后调用 stateChanged 方法,因此标签切换回之前选择的标签并(正确)执行我的动画。

所以标签改变了两次,第一次在setSelectedIndex 之后没有动画,第二次在AnimatedJTabbedPane 中的stateChanged 之后。

我需要类似的东西:

 animatedTabbedPane.firePropertyChange("stateChanged", old, new);

我只想使用stateChanged 方法更改选项卡,所以我可以不使用默认的setSelectedIndex 方法。我该怎么做?

编辑 我的问题的小图表:

红线代表可见性。因此,当用户更改选项卡时,仅调用stateChangedtab 0 平滑地更改为 tab 1。当我使用setSelectedIndex时,tab 0 立即被 tab 1 替换,然后我才能从stateChanged 看到我想要的动画,所以用户会看到一个闪光。

【问题讨论】:

  • 我能看到那个性感的动画代码吗?我的第一个猜测是 stateChanged 不是制作动画的合适位置,因为它发生在选项卡更改之后。也许您可以覆盖 JTabbedPane.setSelectedIndex(int i) 并将动画代码放在超级调用之前,或者直接调用 model.setSelectedIndex(i) 。如果不是这样,您将不得不拦截和处理事件。
  • 我添加了我的 AnimatedTabbedPane 的完整代码。我在互联网上的某个地方找到了这个,但我现在找不到来源。
  • Animator 来自哪个包/项目?
  • timingframeworklink
  • 不错的图片,只是看不出与问题有多大关系 :-) 无论如何,它对我来说很好,所以获得更多帮助的方法是...... SSCCE

标签: java swing events jtabbedpane


【解决方案1】:

我找到了问题的真正原因。

这是线程问题,而不是动画本身。我在 EDT 外打电话给setSelectedIndex,所以我的 JTabbedPane 立即更新,然后执行了来自 EDT 的动画。

答案是:

public void setSelectedTab(final int tab) throws InterruptedException, InvocationTargetException{
    if(!SwingUtilities.isEventDispatchThread()){
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                animatedTabbedPane.setSelectedIndex(tab);
            }
        });
    }
    else
        animatedTabbedPane.setSelectedIndex(tab);
}

EDT 中更改标签不会再导致不需要的闪光。

【讨论】:

    【解决方案2】:

    一种方法是在每个标签的内容中添加AncestorListener。让侦听器触发所需的效果,因为将选项卡添加到或从可见性中删除。

    import java.awt.EventQueue;
    import java.util.Date;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTabbedPane;
    import javax.swing.event.AncestorEvent;
    import javax.swing.event.AncestorListener;
    
    /**
     * @see http://stackoverflow.com/a/17993449/230513
     */
    public class Test {
    
        private void display() {
            JFrame f = new JFrame("Test");
            final JTabbedPane jtp = new JTabbedPane();
            jtp.add("One", createPanel());
            jtp.add("Two", createPanel());
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.add(jtp);
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        }
    
        private JPanel createPanel() {
            JPanel panel = new JPanel();
            final JLabel label = new JLabel(new Date().toString());
            panel.add(label);
            panel.addAncestorListener(new AncestorListener() {
                @Override
                public void ancestorAdded(AncestorEvent event) {
                    // start animation
                    label.setText(new Date().toString());
                }
    
                @Override
                public void ancestorRemoved(AncestorEvent event) {
                    // stop animation
                }
    
                @Override
                public void ancestorMoved(AncestorEvent event) {
                }
            });
            return panel;
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new Test().display();
                }
            });
        }
    }
    

    【讨论】:

    • herehere 看到了一些衰落的方法。
    • 对不起,这不是这个。 setSelectedIndex 仍然打断我的动画。我需要阻止它发生其中一个事件,所以我的效果就像用户用鼠标选择选项卡一样。
    • 您是否同时制作新旧动画以平滑过渡?听起来你正在阻止 EDT。使用javax.swing.Timer 调整动画节奏;在AncestorListener 和任何调用setSelectedIndex() 的人中调用start()stop()
    • 是的,这是 EDT 问题。我在 EDT 外更改选项卡,将我的代码放在 EDT 内解决了问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多