tags:

views:

555

answers:

6

How to add marquee behaviour to text of JLabel?

I have tried this

JLabel search = new JLabel("<html><marquee>Search</marquee><html>");

but its not working.

+1  A: 

As you found, the HTML on JLabel is limited to formatting and doesn't support the <marquee> tag. You would have to use something like a SwingWorker or a ExecutorService to change the text every few milliseconds.

Jeff Walker
another option is javax.swing.Timer
Karussell
+2  A: 

Please see http://forums.sun.com/thread.jspa?forumID=57&amp;threadID=605616 for details about how to do this :)

(Edit: I would probably use System.currentTimeMillis() directly inside the paint() method instead of using a timer, and then divide / modulo (%) it to get it into the required range for 'x offset' on the examples). By increasing the size of the division number, you can change the speed ((System.currentTimeMillis() / 200) % 50).

(Edit 2: I just updated the code below to fix a problem with repainting. Now we schedule a repaint within the paint method; maybe there's a better way but this works :))

(Edit 3: Errr, I tried with a longer string and it messed up. That was easy to fix (increase range by width again to compensate for negative values, subtract by width)

package mt;

import java.awt.Graphics;

import javax.swing.Icon;
import javax.swing.JLabel;

public class MyJLabel extends JLabel {
    public static final int MARQUEE_SPEED_DIV = 5;
    public static final int REPAINT_WITHIN_MS = 5;

    /**
     * 
     */
    private static final long serialVersionUID = -7737312573505856484L;

    /**
     * 
     */
    public MyJLabel() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @param image
     * @param horizontalAlignment
     */
    public MyJLabel(Icon image, int horizontalAlignment) {
        super(image, horizontalAlignment);
        // TODO Auto-generated constructor stub
    }

    /**
     * @param image
     */
    public MyJLabel(Icon image) {
        super(image);
        // TODO Auto-generated constructor stub
    }

    /**
     * @param text
     * @param icon
     * @param horizontalAlignment
     */
    public MyJLabel(String text, Icon icon, int horizontalAlignment) {
        super(text, icon, horizontalAlignment);
        // TODO Auto-generated constructor stub
    }

    /**
     * @param text
     * @param horizontalAlignment
     */
    public MyJLabel(String text, int horizontalAlignment) {
        super(text, horizontalAlignment);
        // TODO Auto-generated constructor stub
    }

    /**
     * @param text
     */
    public MyJLabel(String text) {
        super(text);
    }



    /* (non-Javadoc)
     * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
     */
    @Override
    protected void paintComponent(Graphics g) {
        g.translate((int)((System.currentTimeMillis() / MARQUEE_SPEED_DIV) % (getWidth() * 2)) - getWidth(), 0);
        super.paintComponent(g);
        repaint(REPAINT_WITHIN_MS);
    }
}
Chris Dennett
Hm, and what would you do with that number of millis that you calculate in paint()? Keep in mind that swing painting is single threaded, and thus, any sleeping in the paint() method will hang the rest of the UI. Apologies if I have misinterpreted your response.
Jeff Walker
Edited the original post to reflect what you need :)
Chris Dennett
For measuring elapsed (relative) time, you should use System.nanoTime(). System.currentTimeMillis() give you absolute time according to the system clock. The system clock could abruptly change if an external application changes the system time (e.g. ntp).http://stackoverflow.com/questions/351565/system-currenttimemillis-vs-system-nanotime
Ajay
An additional problem that I found during testing was that if the label was resized (say, when the parent element size is changed), the x position would change wildly, simply because the formula is not incremental. Additionally, the JLabel might get ellipses on the end when the width is to too small, even though it doesn't matter so much to the marquee. I then tried incrementing a number for the x offset in the paintComponent code, but then it still had the ellipses. I finally tried a custom component which worked, but was a bit dodgy with handling the positioning of the text and the look.
Chris Dennett
A: 

Hi, hier is a quick an dirty solution. If you need, I can precise the code in some points.


package gui;

import java.awt.Color;
import java.awt.GridLayout;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;

public class ScrollLabel extends JPanel {
    private static final long   serialVersionUID    = 3986350733160423373L;
    private final JLabel        label;
    private final JScrollPane   sPane;
    private final ScrollMe      scrollMe;

    public ScrollLabel(String text) {
        setLayout(new GridLayout(1, 1));
        label = new JLabel(text);
        sPane = new JScrollPane(label);
        sPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
        JScrollBar horizontalScrollBar = new JScrollBar(JScrollBar.HORIZONTAL);
        sPane.setHorizontalScrollBar(horizontalScrollBar);
        setBorder(BorderFactory.createLineBorder(Color.RED));
        add(sPane.getViewport());
        scrollMe = new ScrollMe(this, label, sPane);
        scrollMe.start();
    }

    class ScrollMe extends Thread {
        JScrollPane sPane;
        ScrollLabel parent;
        JLabel      label;

        ScrollMe(ScrollLabel parent, JLabel label, JScrollPane sPane) {
            this.sPane = sPane;
            this.parent = parent;
            this.label = label;
        }

        boolean isRunning   = true;

        @Override
        public void run() {
            int pos = 0;
            try {
                while (isRunning) {
                    if (sPane.isVisible()) {
                        int sWidth = parent.getSize().width;
                        int lWidth = label.getWidth();

                        if (sWidth < lWidth) {
                            if (pos + sWidth > lWidth)
                                pos = 0;
                            sPane.getHorizontalScrollBar().setValue(pos);
                        }
                    }
                    synchronized (this) {
                        wait(50);
                        pos += 1;
                    }
                }
            }
            catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            finally {
                isRunning = false;
            }
        }
    }

    public static void main(String[] args) {
        JFrame jFrame = new JFrame("ScrollLabel");
        jFrame.getContentPane().add(new ScrollLabel("how to add marquee behaviour to Jlabel !Java"));
        jFrame.setVisible(true);
    }
}

elou
+1  A: 

I found a solution on the web that works at least for me :-)

Karussell
+1  A: 

Hello,

This works great (I wrote this by myself, so there might be some mistakes):

import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;

import javax.swing.JLabel;
import javax.swing.JPanel;

public class JMarqueeLabel extends JPanel implements Runnable
{
    /**
     * 
     */
    private static final long serialVersionUID = -2973353417536204185L;
    private int x;
    private FontMetrics fontMetrics;
    public static final int MAX_SPEED = 30;
    public static final int MIN_SPEED = 1;
    private int speed;
    public static final int SCROLL_TO_LEFT = 0;
    public static final int SCROLL_TO_RIGHT = 1;
    private int scrollDirection = 0;
    private boolean started = false;
    private JLabel label;

    public JMarqueeLabel(String text)
    {
        super();
        label = new JLabel(text)
        {
            /**
             * 
             */
            private static final long serialVersionUID = -870580607070467359L;

            @Override
            protected void paintComponent(Graphics g)
            {
                g.translate(x, 0);
                super.paintComponent(g);
            }
        };
        setLayout(null);
        add(label);
        setSpeed(10);
        setScrollDirection(SCROLL_TO_RIGHT);
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        label.paintComponents(g);
    }

    public void setScrollDirection(int scrollDirection)
    {
        this.scrollDirection = scrollDirection;
    }

    public int getScrollDirection()
    {
        return scrollDirection;
    }

    public void setSpeed(int speed)
    {
        if (speed < MIN_SPEED || speed > MAX_SPEED)
        {
            throw new IllegalArgumentException("speed out of range");
        }
        this.speed = speed;
    }

    public int getSpeed()
    {
        return speed;
    }

    @Override
    public void validateTree()
    {
        System.out.println("Validate...");
        super.validateTree();
        label.setBounds(0, 0, 2000, getHeight());
        if (!started)
        {
            x = getWidth() + 10;
            Thread t = new Thread(this);
            t.setDaemon(true);
            t.start();
            started = true;
        }
    }

    public String getText()
    {
        return label.getText();
    }

    public void setText(String text)
    {
        label.setText(text);
    }

    public void setTextFont(Font font)
    {
        label.setFont(font);
        fontMetrics = label.getFontMetrics(label.getFont());
    }

    @Override
    public void run()
    {
        fontMetrics = label.getFontMetrics(label.getFont());
        try
        {
            Thread.sleep(100);
        } catch (Exception e)
        {
        }
        while (true)
        {
            if (scrollDirection == SCROLL_TO_LEFT)
            {
                x--;
                if (x < -fontMetrics.stringWidth(label.getText()) - 10)
                {
                    x = getWidth() + 10;
                }
            }
            if (scrollDirection == SCROLL_TO_RIGHT)
            {
                x++;
                if (x > getWidth() + 10)
                {
                    x = -fontMetrics.stringWidth(label.getText()) - 10;
                }
            }
            repaint();
            try
            {
                Thread.sleep(35 - speed);
            } catch (Exception e)
            {
            }
        }
    }

}

This works for very long strings. If you need longer strings, than you have to change 2000 to a higher number.

Hope this is what you want :-)

Martijn Courteaux
A: 

Hi

i have tested this program , it is working for Longer String length that exceed out of Frame if i give only ' Welcome to My Home ' String is not moving , really it's good please send me if you have any program for this on [email protected]

thanks, Ranveer Singh kushwah

Ranveer