Removing flashiness/ shakiness from scrolling text - java

I have finally made a ticker that functions how I want it to, but the only problem now is that it seems kind of shaky and looks like it is being displayed on an old tv. How do I make it look smoother?
Here is my code:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Scroll1 extends JPanel{
private int x;
private int x2;
private int y;
private int width;
private String text;
private Font f=(new Font("SansSerif", Font.PLAIN,24));
public Scroll1(int startX)
{
x=Integer.MIN_VALUE;
x2=Integer.MIN_VALUE;
y=150;
text= "We are pleased to announce that Actavis has completed the acquisition of Auden Mckenzie, a dynamic and fast growing company focused on the development, licensing and marketing of generic medicines and proprietary brands in the UK. ";
}
#Override
public void paintComponent(Graphics g)
{
g.setFont(f);
if ( x == Integer.MIN_VALUE ){
width=g.getFontMetrics().stringWidth(text);
x = 0;
}
if ( x2 == Integer.MIN_VALUE ){
x2 = g.getFontMetrics().stringWidth(text);
}
g.setColor(Color.white);
g.fillRect(this.getX(), this.getY(), (int)this.getBounds().getWidth(), (int)this.getBounds().getHeight());
g.setColor(Color.black);
g.drawString(text, x, y);
g.drawString(text, x2, y);
}
public void start() throws InterruptedException{
while(true){
while(x>= (-width)&&x2!=Integer.MIN_VALUE){
x--;
x2--;
y = getHeight()/2;
Thread.sleep(15);
this.
validate();
repaint();
if(x2<=-width)
{
x2=width;
}
}
if(x<-width&&x!=Integer.MIN_VALUE)
{
x=width;
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Scrolling Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Scroll1 scrolling = new Scroll1(-100);
frame.getContentPane().add(scrolling);
frame.setSize(400, 300);
frame.setVisible(true);
try {
scrolling.start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

Call super.paintComponent before you perform custom painting
Prefer Swing Timer of a Thread, as Swing Timers are triggered within the EDT and you run less risk of having dirty reads between the paintComponent method and the Thread
paintComponent really shouldn't be making decisions about the rendering state, instead it should just paint the current state.
You don't need g.fillRect(this.getX(), this.getY(), (int) this.getBounds().getWidth(), (int) this.getBounds().getHeight());, just set the background color of the panel and allow the painting routines to do their jobs. Painting is done from 0x0, been the top/left corner of the component
There is an issue with your while loop in your start method which doesn't seem to allow it to run (at least in my testing)
Can you help me try to transfer it over to a swing timer or give me a good source to do so?
Can you pay me? ;)
First, getting it to be completely "stagger" free isn't going to be possible, as the process is reliant on the system's capability of keeping up to date with your requests. For example, you really don't need to it be running at > 60fps, seriously, 25fps or even 15fps would be more the suitable for what you are trying to do. You control the speed of the scroll by using the movement delta instead
The following example uses a Swing Timer, as did my previous example, but this example will also create a "continuous" loop, where if the amount of text been displayed is less then the width of the visible area, it will add a "new" message to trail it.
This example would be capable of display multiple different messages of varying lengths but I guess, that's a bonus
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Scroll1 extends JPanel {
private String text;
private Font f = (new Font("SansSerif", Font.PLAIN, 24));
private List<TickerTap> tickerTaps;
public Scroll1(int startX) {
text = "We are pleased to announce that Actavis has completed the acquisition of Auden Mckenzie, a dynamic and fast growing company focused on the development, licensing and marketing of generic medicines and proprietary brands in the UK.";
setFont(f);
setBackground(Color.WHITE);
tickerTaps = new ArrayList<>(4);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
FontMetrics fm = getFontMetrics(getFont());
if (tickerTaps.isEmpty()) {
tickerTaps.add(new TickerTap(text, getWidth()));
}
TickerTap taps[] = tickerTaps.toArray(new TickerTap[tickerTaps.size()]);
int visibleWidth = 0;
for (TickerTap tap : taps) {
tap.move();
int messageWidth = fm.stringWidth(tap.getMessage());
if (tap.getxPos() <= -messageWidth) {
tickerTaps.remove(tap);
} else {
visibleWidth += tap.getxPos() + messageWidth;
}
}
while (visibleWidth < getWidth()) {
TickerTap last = tickerTaps.get(tickerTaps.size() - 1);
int xPos = last.getxPos() + fm.stringWidth(last.getMessage());
String message = " * " + text;
TickerTap next = new TickerTap(message, xPos);
visibleWidth += next.getxPos() + fm.stringWidth(next.getMessage());
tickerTaps.add(next);
}
repaint();
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
for (TickerTap tap : tickerTaps) {
FontMetrics fm = g2d.getFontMetrics();
int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g2d.setColor(Color.black);
g2d.drawString(tap.getMessage(), tap.getxPos(), y);
}
}
public class TickerTap {
private String message;
private int xPos;
public TickerTap(String message, int xPos) {
this.message = message;
this.xPos = xPos;
}
public String getMessage() {
return message;
}
public int getxPos() {
return xPos;
}
public void move() {
xPos -= 2;
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Scrolling Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Scroll1 scrolling = new Scroll1(-100);
frame.getContentPane().add(scrolling);
frame.setSize(400, 300);
frame.setVisible(true);
}
});
}
}
Take a look at How to use Swing Timers for more details. Timer is actually really to understand if you simply think of it like a kind of loop

Welp Mad Programmer you are a boss, thanks for all the help. I running what you had and it didnt work, strange huh? Anyway here is what I got now that seems to be working a whole lot smoother but still a few glitches here and there:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Scroll2 extends JPanel implements ActionListener{
private int x;
private int x2;
private int y;
private int width;
private String text;
private Font f=(new Font("SansSerif", Font.BOLD,34));
Timer t;
public Scroll2(int startX)
{
x=Integer.MIN_VALUE;
x2=Integer.MIN_VALUE;
y=150;
text= "We are pleased to announce that Actavis has completed the acquisition of Auden Mckenzie, a dynamic and fast growing company focused on the development, licensing and marketing of generic medicines and proprietary brands in the UK. ";
Timer refreshTimer = new javax.swing.Timer(10, this);
refreshTimer.start();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(f);
if ( x == Integer.MIN_VALUE ){
width=g.getFontMetrics().stringWidth(text);
x = 0;
}
if ( x2 == Integer.MIN_VALUE ){
x2 = g.getFontMetrics().stringWidth(text);
}
g.setColor(Color.white);
g.fillRect(this.getX(), this.getY(), (int)this.getBounds().getWidth(), (int)this.getBounds().getHeight());
g.setColor(Color.black);
g.drawString(text, x, y);
g.drawString(text, x2, y);
}
public void refresh ()
{
if(x>=(-width)&&x2!=Integer.MIN_VALUE)
{
x--;
x2--;
y = getHeight()/2;
}
if(x2<=-width&&x!=Integer.MIN_VALUE)
{
x2=width;
}
if(x<-width&&x!=Integer.MIN_VALUE)
{
x=width;
}
repaint();
}
public static void main(String[] args) {
JFrame frame = new JFrame("Scrolling Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Scroll2 scrolling = new Scroll2(-100);
frame.getContentPane().add(scrolling);
frame.setSize(400, 300);
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
this.refresh();
}
}

Related

How to implement multiple timers in Java Swing

I have a timer for my set of icons which animates these icons. Basically - I have a world map as a background and car,airplane,etc. icons "floating" on this map. I'm using a swing timer to do this, but it makes all the icons to appear and start their motion as soon as the JPanel is rendered. I want them to appear/start their motion at random time, I thought about implementing a 2nd timer and every 10 second create a random number - either 1 or 2 - (e.g and if the number is equal to 1 it starts the animation for this particular icon).
Timer timer;
....
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(world_map,0,0, getWidth(), getHeight(), null);
for (Transportation item : transportations) {
item.drawTransportation(g);
g2d.drawLine(item.start_x+25,item.start_y+25,item.finish_x+25,item.finish_y+25);
}
}
#Override
public void actionPerformed(ActionEvent e) {
for (Transportation item : transportations) {
if (item.x > item.finish_x) {
item.x -= 1;
}
else if (item.x < item.finish_x) {
item.x += 1;
}
if (item.y > item.finish_y) {
item.y -= 1;
}
else if (item.y < item.finish_y) {
item.y += 1;
}
}
repaint();
}
public void startCoords() {
for (Transportation item : transportations) {
item.setMap_coords(map_coords);
item.setCountries(countries);
}
timer = new Timer(10,this);
timer.start();
}
"Generally" speaking, multiple Swing Timers don't scale well. And if you're trying to develop a game of some sort, keeping all the "updates" within a single "game loop" adds a lot of benefit when it comes to keeping things under control.
So, this is a really simply concept. When the Timer ticks, it checks for the amount of time which has passed and when some kind of delta is reached, it generates a bunch of "random things".
I also put a little counter in so you can see that something is actually still happening along the way.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
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() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Point> points = new ArrayList<>(25);
private Timer timer;
private Instant clock;
private Random rnd = new Random();
public TestPane() {
setBackground(Color.BLACK);
setForeground(Color.WHITE);
timer = new Timer(5, new ActionListener() {
private Instant anchorTime;
#Override
public void actionPerformed(ActionEvent arg0) {
if (anchorTime == null) {
anchorTime = Instant.now();
}
// You could just do a mod 10, but I'm feeling lazy
if (Duration.between(anchorTime, Instant.now()).getSeconds() >= 10) {
anchorTime = Instant.now();
generateRandomThing();
}
repaint();
}
});
timer.setInitialDelay(0);
clock = Instant.now();
}
protected void generateRandomThing() {
for (int index = 0; index < 10; index++) {
int x = 10 + (int) (rnd.nextDouble() * (getWidth() - 20));
int y = 10 + (int) (rnd.nextDouble() * (getHeight() - 20));
Point p = new Point(x, y);
points.add(p);
}
}
#Override
public void addNotify() {
super.addNotify();
clock = Instant.now();
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify(); //To change body of generated methods, choose Tools | Templates.
timer.stop();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Duration runTime = Duration.between(clock, Instant.now());
g2d.setColor(Color.WHITE);
FontMetrics fm = g2d.getFontMetrics();
String text = Long.toString(runTime.getSeconds());
int x = 10;
int y = 10 + fm.getAscent();
g2d.drawString(text, x, y);
g2d.setColor(Color.RED);
for (Point p : points) {
g2d.fillOval(p.x - 5, p.y - 5, 10, 10);
}
g2d.dispose();
}
}
}
This is, by no means, the only approach you can take, it's just one of a question of scalability and control.

Creating animated text that loops on itself

I'm trying to make a news ticker type thing where a string is entered and inside of a JPanel the text is looped.
I know it moves currently at about 90 pixels a second and that there is about 16 pixels per char in this font. So what I am asking is how I can use this information to make it so that after a timer runs for a certain time, a new animated text is spawned, and how after the 1st animated text leaves the screen completely, how to delete it from the memory.
This is what I got so far, the code is borrowed heavily from here : Java Animate JLabel, So also if you see any unneeded code in there, let me know.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.EnumMap;
import javax.swing.*;
#SuppressWarnings("serial")
public class AnimateExample extends JPanel {
private static final int TIMER_DELAY = 20;
private static final String KEY_DOWN = "key down";
public static final int TRANSLATE_SCALE =2;
private static final Font BG_STRING_FONT = new Font(Font.SANS_SERIF,
Font.BOLD, 32);
private EnumMap<Direction, Boolean> dirMap =
new EnumMap<AnimateExample.Direction, Boolean>(Direction.class);
private BufferedImage image = null;
private int posX = 100;
private int posY = 50;
Timer t;
public AnimateExample() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.TRUE);
}
t = new Timer(TIMER_DELAY, new TimerListener());
t.start();
ActionMap actionMap = getActionMap();
for (final Direction dir : Direction.values()) {
actionMap.put(dir.Left + KEY_DOWN, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
dirMap.put(dir, true);
}
});
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setFont(BG_STRING_FONT);
g.setColor(Color.LIGHT_GRAY);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
String s = "Hi, I'm trying to make a news ticker type thing where a string is entered and inside of a JPanel the text is looped.I know it moves currently at about 90 pixels a second and that there is about 16 pixels per char in this font. So what I am asking is how I can use this information to make it so that after a timer runs for a certain time, a new animated text is spawned, and how after the 1st animated text leaves the screen completely, how to delete it from the memory.";
g.drawString(s, posX, posY);
}
private class TimerListener implements ActionListener {
public void actionPerformed(java.awt.event.ActionEvent e) {
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
posX += dir.getX() * TRANSLATE_SCALE;
posY += dir.getY() * TRANSLATE_SCALE;
}
}
repaint();
if(posX<-500)
{
t.stop();
}
};
}
enum Direction {
Left( KeyEvent.VK_LEFT, -1, 0);
private int keyCode;
private int x;
private int y;
private Direction(int keyCode, int x, int y) {
this.keyCode = keyCode;
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
private static void createAndShowGui() {
AnimateExample mainPanel = new AnimateExample();
JFrame frame = new JFrame("Animate Example");
frame.setUndecorated(true);
frame.setSize(1600, 900);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.add(mainPanel);
mainPanel.setBounds(new Rectangle(1600,400));
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
EDIT:
I was also thinking that perhaps resetting the X position after a certain amount of time or distance, but the problem with that would be that it would require that the area would be blank for one screen length. If you have a work around for that, it would also be great.
You could check out the Marquee Panel. It uses a different approach than most scrollers. You add actual components to the panel and the components scroll.
This allows you to scroll text or images. You can use labels with HTML so you can have colored text etc. The message will continue to scroll until you manually stop the scrolling.
Currently there is no automatic mechanism for replacing a scrolling message. Although you could easily create a List or Queue of messages. Then when a message has finished scrolling completely you just remove the current message and add a new one.
Following is the section code from the MarqueePanel class you would need to change:
public void actionPerformed(ActionEvent ae)
{
scrollOffset = scrollOffset + scrollAmount;
int width = super.getPreferredSize().width;
if (scrollOffset > width)
{
scrollOffset = isWrap() ? wrapOffset + scrollAmount : - getSize().width;
// add code here to swap component from the List or Queue
}
repaint();
}
Of course you would also need to add a method to the class to add a component to the List or Queue.
Don't worry about the character width, as different fonts can produce variable character widths. Instead use FontMetrics to measure the String width and determine it the xPos <= -stringWidth, this is when the text would be fully off the left hand side of the screen.
You could use a Queue of some kind to manage text, popping of the next one as you need it. This example simply pops the last message onto the end of the queue, so it will keep repeating
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Queue<String> queue = new LinkedList<>();
queue.add("I have something to say, it's better to burn out then fade away");
queue.add("Banana peels");
queue.add("Don't worry if plan A fails, there are 25 more letters in the alphabet");
queue.add("When the past comes knocking, don't answer. It has nothing new to tell you");
queue.add("I know the voices in my head aren't real..... but sometimes their ideas are just absolutely awesome!");
TickerTapPane pane = new TickerTapPane();
pane.setMessages(queue);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(pane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TickerTapPane extends JPanel {
private Queue<String> queue;
private String message;
private int xPos;
public TickerTapPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (message == null) {
message = queue.remove();
xPos = getWidth();
}
xPos -= 4;
FontMetrics fm = getFontMetrics(getFont());
int stringWidth = fm.stringWidth(message);
if (xPos <= -stringWidth) {
queue.add(message);
xPos = getWidth();
message = queue.remove();
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (message != null) {
Graphics2D g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
int yPos = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g2d.drawString(message, xPos, yPos);
g2d.dispose();
}
}
protected void setMessages(Queue<String> queue) {
this.queue = queue;
}
}
}

How can I draw an image part by part?

class DrawIma extends JPanel{
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i=0;i<20;i++){
for (int j=0;j<20;j++) {
g.drawImage(BuArr[i*20+j], 20*i, 20*j, 20, 20, null);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
In this part, BuArr are the 400 blocks divided from a BufferedImage, now i want them to be draw one by one, but the method can not draw the blocks separately, how can i do this?
Swing is single thread and not thread safe.
This means that you should not perform any long running or blocking (Thread.sleep) operations within the IU thread (the Event Dispatching Thread). It also means that you can not update, modify or create UI elements outside of the EDT context.
Instead, use a Swing Timer to generate a repeated callback at a specified interval and render the portions of the image to something like a BufferedImage, which you can the paint to the component via its paintComponent method...
See Concurrency in Swing and How to use Swing Timers for more details
Because it was a good time waster
This generates a List of Rectangles which represent the individual blocks I want to paint, I then randomise the List and run the Timer, picking the top most Rectangle off the List and using BufferedImage#getSubImage to draw it from the master to the buffer, which gets painted to the screen...
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestImage {
public static void main(String[] args) {
new TestImage();
}
public TestImage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage master;
private BufferedImage copy;
private List<Rectangle> blocks;
public TestPane() {
setBackground(Color.BLACK);
try {
master = ImageIO.read(new File("..."));
copy = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = copy.createGraphics();
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f);
g2d.setComposite(composite);
g2d.setColor(new Color(0, 0, 0, 0));
g2d.fillRect(0, 0, master.getWidth(), master.getHeight());
g2d.dispose();
int blockSize = 40;
int width = master.getWidth();
int height = master.getHeight();
float aspect = Math.min(width, height) / (float) Math.max(width, height);
int blockHeight = blockSize;
blocks = new ArrayList<>(blockSize * 2);
for (int y = 0; y < master.getHeight(); y += blockHeight) {
if (y + blockHeight > master.getHeight()) {
blockHeight = master.getHeight() - y;
}
int blockWidth = blockSize;
for (int x = 0; x < master.getWidth(); x += blockWidth) {
if (x + blockWidth > master.getWidth()) {
blockWidth = master.getWidth() - x;
}
Rectangle block = new Rectangle(x, y, blockWidth, blockHeight);
blocks.add(block);
}
}
Collections.shuffle(blocks);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (blocks.isEmpty()) {
((Timer) e.getSource()).stop();
} else {
Graphics2D g2d = copy.createGraphics();
Rectangle block = blocks.remove(0);
g2d.drawImage(master.getSubimage(block.x, block.y, block.width, block.height), block.x, block.y, TestPane.this);
g2d.dispose();
repaint();
}
}
});
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return master == null ? new Dimension(200, 200) : new Dimension(master.getWidth(), master.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (copy != null) {
int x = (getWidth() - copy.getWidth()) / 2;
int y = (getHeight() - copy.getHeight()) / 2;
g2d.drawImage(copy, x, y, this);
}
g2d.dispose();
}
}
}

paint() does not get called in JLabel animation

I want to have a bubbling effect - some image with score bubbling from bottom to up. Below is the complete code. The problem is that paint() never gets called by repaint(). I am not good at java awt or swing. Any reason?
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
* The Bubble object for implementing some animations for each block of LOVE pieces
*/
class Bubble extends JLabel {
private static final long serialVersionUID = 1L;
boolean isBubbling = false;
int xpos;
int ypos;
float transparency;
String text = "+100";
GameSettings config;
AlphaComposite transparency05=AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
Bubble(int x, int y) {
isBubbling=false;
this.xpos = x;
this.ypos = y;
}
void Bubbling() {
isBubbling=true;
for(int i=1;i<=50; i+=4) {
System.out.println("Bubbling init");
ypos -= (int)(7.2*i);
transparency = transparency/2;
setFont(new Font("arial, helvetica",Font.BOLD|Font.ITALIC,i));
repaint();
try {Thread.sleep(15);} catch(Throwable e) {}
}
new Thread() {
public void run() {
for(int i=50;i>=0; i-=4) {
System.out.println("Bubbling run");
ypos -= (int)(7.2*i);
transparency = transparency/2;
setFont(new Font("arial, helvetica",Font.BOLD|Font.ITALIC,i));
repaint();
try {Thread.sleep(15);} catch(Throwable e) {}
}
isBubbling=false;
repaint();
}
}.start();
}
#Override
public void paint(Graphics g1) {
super.paint(g1);
System.out.println("Bubbling paint begin");
if(isBubbling) {
System.out.println("Bubbling paint");
Graphics2D g=(Graphics2D) g1;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
FontMetrics fm=g.getFontMetrics();
Rectangle r = getBounds();
int width=fm.stringWidth(text);
int height=fm.getHeight()*4/5;
int cx=g.getFont().getSize()/10;
int x=(r.width-width)/2;
int xx=fm.charWidth('i');
//g.setComposite(transparency05);
g.setComposite(AlphaComposite.SrcOver);
g.setColor(Color.red);
//g.drawString(text,xpos*config.pieceWidth,ypos*config.pieceHeight);
g.drawString(text,xpos,ypos);
//Image img=Tetris.getResource().getImage(config.imagePath+config.imagePrefix+"heart"+config.imagePostfix);
//g.drawImage(img,img.getWidth(null)+3,0,null);
//g.drawImage(img,xpos*config.pieceWidth,ypos*config.pieceHeight,null);
}
}
//for test
static public void main(String[] args) {
//Create and set up the window.
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JPanel newContentPane = new JPanel();
newContentPane.setOpaque(true); //content panes must be opaque
Bubble bub = new Bubble(50, 50);
newContentPane.add(bub);
frame.setContentPane(newContentPane);
frame.setPreferredSize(new Dimension(300, 300));
//Display the window.
frame.pack();
frame.setVisible(true);
bub.Bubbling();
}
}
There are many issues in your code, but the main reason why paint() is not invoked, is because your Bubble component has a size of 0 x 0. This is because you did not override getPreferredSize() to return an adequate value. Moreover, your ypos variable becomes negative in no time meaning that you don't have the time to see the animation.
Now, you should really consider taking care of the following other issues:
Override rather paintComponent than paint
Make sure that you never sleep on the EDT (here it does not happen because you are quite lucky)
Start your UI from the EDT with a SwingUtilities.invokeLater call.
Why do you create another thread in your Bubbling method remains a mistery to me (either do it from the start, for the entire method, or don't do it at all)
a javax.swing.Timer would be much more appropriate in your situation
Follow java naming conventions (methods start with a lower-case letter)
Here is an update of your code (but I did not changed everything according the remarks above):
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
* The Bubble object for implementing some animations for each block of LOVE pieces
*/
class Bubble extends JLabel {
private static final long serialVersionUID = 1L;
boolean isBubbling = false;
int xpos;
int ypos;
float transparency;
String text = "+100";
AlphaComposite transparency05 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
Bubble(int x, int y) {
isBubbling = false;
this.xpos = x;
this.ypos = y;
}
void Bubbling() {
isBubbling = true;
for (int i = 1; i <= 50; i += 4) {
System.out.println("Bubbling init");
ypos--;
transparency = transparency / 2;
setFont(new Font("Arial", Font.BOLD | Font.ITALIC, i));
repaint();
try {
Thread.sleep(50);
} catch (Throwable e) {
}
}
new Thread() {
#Override
public void run() {
for (int i = 50; i >= 0; i -= 4) {
System.out.println("Bubbling run");
ypos--;
transparency = transparency / 2;
setFont(new Font("Arial", Font.BOLD | Font.ITALIC, i));
repaint();
try {
Thread.sleep(50);
} catch (Throwable e) {
}
}
isBubbling = false;
repaint();
}
}.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(180, 50);
}
#Override
protected void paintComponent(Graphics g1) {
super.paintComponent(g1);
if (isBubbling) {
System.out.println("Bubbling paint");
Graphics2D g = (Graphics2D) g1;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
FontMetrics fm = g.getFontMetrics();
Rectangle r = getBounds();
int width = fm.stringWidth(text);
int height = fm.getHeight() * 4 / 5;
int cx = g.getFont().getSize() / 10;
int x = (r.width - width) / 2;
int xx = fm.charWidth('i');
// g.setComposite(transparency05);
// g.setComposite(AlphaComposite.SrcOver);
g.setColor(Color.red);
// g.drawString(text,xpos*config.pieceWidth,ypos*config.pieceHeight);
System.err.println(xpos + " " + ypos);
g.drawString(text, xpos, ypos);
// Image img=Tetris.getResource().getImage(config.imagePath+config.imagePrefix+"heart"+config.imagePostfix);
// g.drawImage(img,img.getWidth(null)+3,0,null);
// g.drawImage(img,xpos*config.pieceWidth,ypos*config.pieceHeight,null);
}
}
// for test
static public void main(String[] args) {
// Create and set up the window.
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create and set up the content pane.
JPanel newContentPane = new JPanel(new FlowLayout());
newContentPane.setOpaque(true); // content panes must be opaque
Bubble bub = new Bubble(50, 50);
newContentPane.add(bub);
frame.setContentPane(newContentPane);
frame.setPreferredSize(new Dimension(300, 300));
// Display the window.
frame.pack();
frame.setVisible(true);
bub.Bubbling();
}
}
The problem is that your label no Dimension. You can set with setPreferredSize()
public static final Dimension PREF_SIZE = new Dimension(70, 70);
//in main method or you can override `getPreferredSize()`
Bubble bub = new Bubble(50, 50);
bub.setPreferredSize(PREF_SIZE);
Your are sleeping The Event Dispatch Thread. All gui stuff must be executed in that thread, instead of that take a look to Swing Timers.
Also you should override paintComponent() instead of paint() for swing components. Read more in this article

Rotating wheel in Swing

I have searched here but did not get my answer (though I feel it should be here).
I want to perform some activity(but I don't know how much time it will take to complete) and while this task is being run, I want to show a rotating wheel to the user with a message "Processing...please wait". Once the activity gets completed,rotating wheel should also disappear.
How to implement this feature?
As I seem to recall, the GlassPane in Swing can be used to:
Intercept user input.
Display an image/animation
Someone already implemented this: http://www.curious-creature.org/2005/02/15/wait-with-style-in-swing/
There is a download link for the source code at the top of the page.
Also look at: http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html
It explains the GlassPane in some more detail.
A lot will come down to what it is you want to achieve and how much customization and work your want to go to.
You could just load a gif with ImageIcon and apply it to a JLabel to achieve the effect you're after. This example demonstrates the use of custom painting and a javax.swing.Timer to animate the wait cycle.
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.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Arc2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Waiting {
public static void main(String[] args) {
new Waiting();
}
public Waiting() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Timer paintTimer;
private float cycle;
private boolean invert = false;
public TestPane() {
paintTimer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cycle += 0.05f;
if (cycle > 1f) {
cycle = 0f;
invert = !invert;
}
repaint();
}
});
paintTimer.setRepeats(true);
setRuning(true);
}
public void setRuning(boolean running) {
if (running) {
paintTimer.start();
} else {
paintTimer.stop();
}
}
public boolean isRunning() {
return paintTimer.isRunning();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(40, 40);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (isRunning()) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int width = getWidth() - 1;
int height = getHeight() - 1;
int radius = Math.min(width, height);
int x = (width - radius) / 2;
int y = (height - radius) / 2;
int start = 0;
int extent = Math.round(cycle * 360f);
if (invert) {
start = extent;
extent = 360 - extent;
}
g2d.setColor(Color.RED);
g2d.fill(new Arc2D.Float(x, y, radius, radius, start, extent, Arc2D.PIE));
g2d.setColor(Color.YELLOW);
g2d.draw(new Arc2D.Float(x, y, radius, radius, start, extent, Arc2D.PIE));
g2d.dispose();
}
}
}
}
In order for Swing to continue painting, you will need to create a background thread of some kind to perform the work outside of the Event Dispatching Thread until you are finished...
For example
SwingWorker worker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
// Done some work...
}
#Override
protected void done() {
// stop the animation...
}
};
For more details, check out Concurrency in Swing for more details

Categories

Resources