JTabbedPane上的fire stateChanged事件

我有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); } 

但是现在tab在setSelectedIndex结束后立即更改 – 没有动画,然后调用stateChanged方法,因此Tab切换回选择的先前选项卡并执行(正确)我的动画。

因此,选项卡正在更改两次,首先是在setSelectedIndex之后没有动画,第二次是在AnimatedJTabbedPane stateChanged之后。

我需要这样的东西:

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

我想只使用stateChanged方法更改选项卡,所以我可以不使用默认的setSelectedIndex方法。 我怎样才能做到这一点?

编辑我的问题的一个小图:

在此处输入图像描述

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

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

这是线程问题,而不是动画本身。 我在EDT之外调用了setSelectedIndex因此我的JTabbedPane立即更新,然后执行了EDT的动画。

该anserw是:

 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内部的标签不再需要不必要的闪光灯了。

一种方法是将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(); } }); } }