0

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.

Community
  • 1
  • 1
Cole Gordon
  • 155
  • 1
  • 17
  • You need to know how long the text would be when rendered. Don't trust what single characters width is, they could all be different. Instead use something like FontMetrcis#stringWidth. Once the x position is -stringWidth, then you can remove the old text and add new text. Personally, I'd avoid using JLabel, as it has its own text management system, which ute not using, which would only confuse issues – MadProgrammer Jun 03 '15 at 20:37
  • [For example](http://stackoverflow.com/questions/13964877/text-banner-applet-reverses-that-displayes-text/13964991#13964991) – MadProgrammer Jun 03 '15 at 20:38
  • As to how you might manage the messages, I'd probably have some kind of queue, which, when you needed, you could pop off the next message from. If required, you could even push the old text back onto the end of the queue – MadProgrammer Jun 03 '15 at 20:40
  • From that approach it makes the text extremely jumpy. I tried something similar to that earlier, and although I got it to loop, the jumpiness made it sickening to read. Is there a way to make it smoother? – Cole Gordon Jun 03 '15 at 21:04
  • Really, because the last example is pretty much what you're doing – MadProgrammer Jun 03 '15 at 22:13
  • `I dont want a message to have to disappear before it loops. Would you know how to do this` - answer was already given yesterday. – camickr Jun 05 '15 at 00:21

2 Answers2

2

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.

camickr
  • 321,443
  • 19
  • 166
  • 288
1

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

enter image description here

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;
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thats not actually as jumpy as I thought, I actually have been working at a different approach but it has the same problem as this. I dont want a message to have to disappear before it loops. Would you know how to do this> – Cole Gordon Jun 04 '15 at 15:19
  • When do you want the next message to start? The jumpiness will be based on the movement delta and time, for example, you could keep the same movement delta, but increase the time (longer delay) and should still look okay – MadProgrammer Jun 04 '15 at 21:58
  • It should be a continuous line of text. Any way I have went with a similar but slightly different approach, but now im having trouble making it look smooths. It moves smoothly enough but everyonce in a while there is a horizantal line that moves through the text real quick, making it look kind of ugly. If you could help here: http://stackoverflow.com/questions/30654392/removing-flashiness-shakiness-from-scrolling-text that would be awesome. – Cole Gordon Jun 04 '15 at 22:01