我正在使用Java GUI制作游戏,而我正试图做一些渐弱的动画。我知道使用Java图形要容易得多,但为此,我正在使用JLabels和Swing,所以我无法真正做到这一点。我试图做的褪色动画是我将JLabel的背景颜色逐渐改变为从一种颜色到下一种颜色的地方,所以我尝试这样做的方法是创建一个颜色数组并拥有一个颜色TimerTask将JLabel的背景逐个设置为每种颜色。
TimerTask task = new TimerTask() {
@Override
public void run() {
setBackground(c);
}
};
tasks[i] = task;
这是我的TimerTask。颜色c是我在这里作为颜色的示例,颜色部分介于我的原始颜色和最终颜色之间。例如,如果我试图从黑色变为白色,理想情况下,程序首先将颜色设置为深灰色,然后是中灰色,然后是浅灰色,最后是最终的白色。我测试了那些TimerTasks,它们似乎没有任何问题,所以我尝试使用Timer逐个执行它们,但它似乎并不是每次都执行。
Timer timer = new Timer();
for (int i = 0; i < f; i++) {
timer.schedule(tasks[i], delay);
}
通常最终会发生的是颜色部分消失,而JLabel最终会卡在原始颜色和最终颜色之间的某种颜色上,好像并非所有已安排的任务都已完成。我做错了什么,尝试用JLabels动画是否合情合理?提前致谢!
编辑:对不起,我之前的描述不太清楚。也许如果我提供原始代码的某些部分可能会更容易,我会尝试解释我尝试让它一点一点地做的事情。
/** Create array of colors **/
Color[] animationColors = new Color[f]; // f is the number of frames
// find difference between old and new values of r, g, b
int diffR = newColor.getRed() - oldColor.getRed();
int diffG = newColor.getGreen() - oldColor.getGreen();
int diffB = newColor.getBlue() - oldColor.getBlue();
// fill array with colors
for (int i = 0; i < f; i++) {
int newR = (int)(diffR * (i + 1) / f) + oldColor.getRed();
int newG = (int)(diffG * (i + 1) / f) + oldColor.getGreen();
int newB = (int)(diffB * (i + 1) / f) + oldColor.getBlue();
Color c = new Color(newR, newG, newB);
animationColors[i] = c;
}
/** Set new background after each delay **/
int delay = (int)(t / f); // t is the time that the animation will last in total
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
animationCount++;
if (animationCount == animationColors.length) {
animationCount = 0;
timer.cancel();
} else {
setBackground(animationColors[animationCount]);
}
}
};
timer.schedule(task, delay, delay);
首先,我尝试通过创建JLabel将在每个短时间间隔后设置的颜色数组来进行淡化动画。我指定了动画将要发生的多个帧,并且在第1帧,JLabel的颜色将设置为位置1的animationColors,第2帧的颜色将设置为位置2处的animationColors等。每帧从第一种颜色到最终颜色逐渐消失,从黑色到白色的淡入淡出将从深灰色开始,然后是中灰色,浅灰色,最后是白色。
这里的问题是,通常,瓷砖的颜色不会完全褪色。有时,它消失的颜色最终变成原始颜色而不是我设置的最终颜色。问题可能与我正在编写计时器的方式有关,或者在GUI应用程序中使用计时器一般是个坏主意?到目前为止,我已经阅读了对这个问题的回答,我对其中的一些内容并不十分了解,所以如果你能解释一下这些问题也会受到赞赏。
许多Swing组件不能很好地处理半透明,特别是在它发生变化时。
下面是一个将标签绘制为BufferedImage
的示例,然后可以将其用作另一个标签的图标。使用这种方法会失去标准标签的许多功能,但可能就足够了。
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.net.*;
public class FadingLabel {
private JComponent ui = null;
private BufferedImage fadeImage;
private BufferedImage clearImage;
private JLabel opaqueLabel;
private JLabel fadeLabel;
FadingLabel() {
try {
initUI();
} catch (MalformedURLException ex) {
ex.printStackTrace();
}
}
public final void initUI() throws MalformedURLException {
if (ui!=null) return;
ui = new JPanel(new BorderLayout(4,4));
ui.setBorder(new EmptyBorder(4,4,4,4));
URL url = new URL("https://i.stack.imgur.com/F0JHK.png");
initialiseImages(url, "Fade me!");
fadeLabel = new JLabel(new ImageIcon(fadeImage));
ui.add(fadeLabel);
ActionListener fadeListener = new ActionListener() {
float transparency;
float difference = .1f;
@Override
public void actionPerformed(ActionEvent e) {
transparency += difference;
if (transparency>=1f) {
difference = -.01f;
transparency = 1f;
}
if (transparency<=0f) {
difference = .01f;
transparency = 0f;
}
fadeImage(transparency);
fadeLabel.repaint();
}
};
Timer timer = new Timer(30, fadeListener);
timer.start();
}
private void fadeImage(float transparency) {
Dimension d = opaqueLabel.getSize();
fadeImage.setData(clearImage.getData());
Graphics2D g = fadeImage.createGraphics();
Composite composite = AlphaComposite.getInstance(AlphaComposite.SRC, transparency);
g.setComposite(composite);
opaqueLabel.paint(g);
}
private void initialiseImages(URL iconURL, String text) {
ImageIcon ii = new ImageIcon(iconURL);
opaqueLabel = new JLabel(text, ii, SwingConstants.LEADING);
opaqueLabel.setForeground(Color.BLUE);
opaqueLabel.setSize(opaqueLabel.getPreferredSize());
Dimension d = opaqueLabel.getSize();
clearImage = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
fadeImage = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
Graphics g = fadeImage.getGraphics();
opaqueLabel.paint(g);
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
FadingLabel o = new FadingLabel();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}
@AndrewThompson,@ TheUltimateMonkey说没有图形,但这是一个很好的答案,但是没有图形,使用这个:
try {
JFrame frm = new JFrame("Fading Label");
JLabel lbl = new JLabel("The background is changing!");
lbl.setOpaque(true);
lbl.setBackground(new Color(0, 0, 0));
frm.add(lbl);
frm.setDefaultCloseOperation(3);
frm.pack();
frm.setVisible(true);
for(int a = 0; a < 256; a++) {
lbl.setBackground(new Color(a, a, a));
Thread.sleep(10);
}
}
catch(Exception e) {}
但是你想尝试java.util.Timer
,然后用这个:
try {
JFrame frm = new JFrame("Fading Label");
JLabel lbl = new JLabel("The background is changing!");
lbl.setOpaque(true);
lbl.setBackground(new Color(0, 0, 0));
frm.add(lbl);
frm.setDefaultCloseOperation(3);
frm.pack();
frm.setVisible(true);
Timer tmr = new Timer();
tmr.schedule(new TimerTask() {
@Override
public void run() {
int a = lbl.getBackground().getRed();
lbl.setBackground(new Color(a + 1, a + 1, a + 1));
if(a == 254) System.exit(0); //This prevents the app from crashing, you can do something else to fix this.
}
}, 10, 10);
}
catch(Exception e) {}