Имам 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
и втори път след stateChanged
в AnimatedJTabbedPane
.
Имам нужда от нещо като:
animatedTabbedPane.firePropertyChange("stateChanged", old, new);
Искам да сменям раздели само с метода stateChanged
, така че мога да направя без метода setSelectedIndex
по подразбиране. Как мога да направя това?
РЕДАКТИРАНЕ Малка графика на моя проблем:
Червената линия представлява видимост. Така че, когато потребител промени раздела, се извиква само stateChanged
, раздел 0 плавно се променя на раздел 1. Когато използвам setSelectedIndex
раздел 0 незабавно се заменя с раздел 1 и едва тогава мога да видя желаната от мен анимация от stateChanged
, така че потребителите да виждат светкавица.
timingframework
връзка - person Demiurg   schedule 01.08.2013