Efecto de marquesina en Java Swing

¿Cómo puedo implementar el efecto Marquee en Java Swing?

Aquí hay un ejemplo usando javax.swing.Timer .

Marquee.png

 import java.awt.EventQueue; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.Timer; /** @see http://stackoverflow.com/questions/3617326 */ public class MarqueeTest { private void display() { JFrame f = new JFrame("MarqueeTest"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); String s = "Tomorrow, and tomorrow, and tomorrow, " + "creeps in this petty pace from day to day, " + "to the last syllable of recorded time; ... " + "It is a tale told by an idiot, full of " + "sound and fury signifying nothing."; MarqueePanel mp = new MarqueePanel(s, 32); f.add(mp); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); mp.start(); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { new MarqueeTest().display(); } }); } } /** Side-scroll n characters of s. */ class MarqueePanel extends JPanel implements ActionListener { private static final int RATE = 12; private final Timer timer = new Timer(1000 / RATE, this); private final JLabel label = new JLabel(); private final String s; private final int n; private int index; public MarqueePanel(String s, int n) { if (s == null || n < 1) { throw new IllegalArgumentException("Null string or n < 1"); } StringBuilder sb = new StringBuilder(n); for (int i = 0; i < n; i++) { sb.append(' '); } this.s = sb + s + sb; this.n = n; label.setFont(new Font("Serif", Font.ITALIC, 36)); label.setText(sb.toString()); this.add(label); } public void start() { timer.start(); } public void stop() { timer.stop(); } @Override public void actionPerformed(ActionEvent e) { index++; if (index > s.length() - n) { index = 0; } label.setText(s.substring(index, index + n)); } } 

Sé que esta es una respuesta tardía, pero acabo de ver otra pregunta sobre una marquesina que se cerró porque se consideró un duplicado de esta respuesta.

Así que pensé en agregar mi sugerencia, que toma un enfoque diferente de las otras respuestas sugeridas aquí.

El MarqueePanel desplaza los componentes en un panel, no solo el texto. Esto le permite aprovechar al máximo cualquier componente de Swing. Se puede usar una marquesina simple agregando un JLabel con texto. Una marquesina más elegante podría usar un JLabel con HTML para que pueda usar diferentes fonts y colores para el texto. Incluso puede agregar un segundo componente con una imagen.

Un progtwig de swing de java para marcar un texto

Acabo de buscar en Google y encontré este enlace . Ejecuté el código y parece hacer lo que quieres.

La respuesta básica es dibujar su texto / gráfico en un bitmap y luego implementar un componente que pinta el desplazamiento del bitmap en cierta cantidad. Por lo general, las marquesinas / tickers se desplazan hacia la izquierda, por lo que el desplazamiento aumenta, lo que significa que el bitmap está pintado en -offset. Su componente ejecuta un temporizador que se dispara periódicamente, incrementando el desplazamiento e invalidando para que se vuelva a pintar.

Cosas como envolver son un poco más complejas de manejar pero bastante sencillas. Si el desplazamiento excede el ancho del bitmap, lo restablece a 0. Si el ancho del desplazamiento + ancho del componente> bitmap pinta el rest del componente comenzando desde el principio del bitmap.

La clave para un ticker decente es hacer que el desplazamiento sea lo más suave y sin parpadeos posible. Por lo tanto, puede ser necesario considerar duplicar el resultado, primero pintando el bit de desplazamiento en un bitmap y luego renderándolo de una vez en vez de dibujar directamente en la pantalla.

Aquí hay un código que armé para que comiences. Normalmente tomaría el código ActionListener y lo MarqueeController en algún tipo de clase MarqueeController para mantener esta lógica separada del panel, pero esa es una pregunta diferente sobre la organización de la architecture MVC, y en una clase tan simple como esta puede no ser tan importante. .

También hay varias bibliotecas de animación que lo ayudarán a hacer esto, pero normalmente no me gusta incluir bibliotecas en proyectos solo para resolver un problema como este.

 la clase pública MarqueePanel amplía JPanel {
   JLabel textLabel privado;
   private int panelLocation;
   private ActionListener taskPerformer;
   private boolean isRunning = false;

   public static final int FRAMES_PER_SECOND = 24;
   public static final int MOVEMENT_PER_FRAME = 5;

   / **
    * El constructor de clase crea un panel de marquesina.
    * /

   public MarqueePanel () {
     this.setLayout (null);
     this.textLabel = new JLabel ("Desplazamiento de texto aquí");
     this.panelLocation = 0;
     this.taskPerformer = new ActionListener () {
       public void actionPerformed (ActionEvent evt) {
         MarqueePanel.this.tickAnimation ();
       }
     }
   }

   / **
    * Inicia la animación.
    * /

   public void start () {
     this.isRunning = verdadero;
     this.tickAnimation ();
   }

   / **
    * Detiene la animación.
    * /

   parada de vacío público () {
     this.isRunning = false;
   }

   / **
    * Mueve la etiqueta un cuadro a la izquierda.  Si está fuera del rango de visualización, vuelva a moverlo
    * a la derecha, fuera del rango de visualización.
    * /

   tick void privadoAnimación () {
     this.panelLocation - = MarqueePanel.MOVEMENT_PER_FRAME;
     if (this.panelLocation 

Agregue un JLabel a su marco o panel.

 ScrollText s= new ScrollText("ello Everyone."); jLabel3.add(s); public class ScrollText extends JComponent { private BufferedImage image; private Dimension imageSize; private volatile int currOffset; private Thread internalThread; private volatile boolean noStopRequested; public ScrollText(String text) { currOffset = 0; buildImage(text); setMinimumSize(imageSize); setPreferredSize(imageSize); setMaximumSize(imageSize); setSize(imageSize); noStopRequested = true; Runnable r = new Runnable() { public void run() { try { runWork(); } catch (Exception x) { x.printStackTrace(); } } }; internalThread = new Thread(r, "ScrollText"); internalThread.start(); } private void buildImage(String text) { RenderingHints renderHints = new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); renderHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); BufferedImage scratchImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); Graphics2D scratchG2 = scratchImage.createGraphics(); scratchG2.setRenderingHints(renderHints); Font font = new Font("Serif", Font.BOLD | Font.ITALIC, 24); FontRenderContext frc = scratchG2.getFontRenderContext(); TextLayout tl = new TextLayout(text, font, frc); Rectangle2D textBounds = tl.getBounds(); int textWidth = (int) Math.ceil(textBounds.getWidth()); int textHeight = (int) Math.ceil(textBounds.getHeight()); int horizontalPad = 600; int verticalPad = 10; imageSize = new Dimension(textWidth + horizontalPad, textHeight + verticalPad); image = new BufferedImage(imageSize.width, imageSize.height, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); g2.setRenderingHints(renderHints); int baselineOffset = (verticalPad / 2) - ((int) textBounds.getY()); g2.setColor(Color.BLACK); g2.fillRect(0, 0, imageSize.width, imageSize.height); g2.setColor(Color.GREEN); tl.draw(g2, 0, baselineOffset); // Free-up resources right away, but keep "image" for // animation. scratchG2.dispose(); scratchImage.flush(); g2.dispose(); } public void paint(Graphics g) { // Make sure to clip the edges, regardless of curr size g.setClip(0, 0, imageSize.width, imageSize.height); int localOffset = currOffset; // in case it changes g.drawImage(image, -localOffset, 0, this); g.drawImage(image, imageSize.width - localOffset, 0, this); // draw outline g.setColor(Color.black); g.drawRect(0, 0, imageSize.width - 1, imageSize.height - 1); } private void runWork() { while (noStopRequested) { try { Thread.sleep(10); // 10 frames per second // adjust the scroll position currOffset = (currOffset + 1) % imageSize.width; // signal the event thread to call paint() repaint(); } catch (InterruptedException x) { Thread.currentThread().interrupt(); } } } public void stopRequest() { noStopRequested = false; internalThread.interrupt(); } public boolean isAlive() { return internalThread.isAlive(); } } 

Se supone que esto es una mejora de @camickr MarqueePanel. Por favor mira arriba.

Para asignar eventos de mouse a los componentes específicos agregados a MarqueePanel

Anular add(Component comp) de MarqueePanel para dirigir todos los eventos del mouse de los componentes

Un problema aquí es qué hacer con los MouseEvents disparados desde los componentes individuales. Mi enfoque es eliminar los oyentes del mouse de los componentes agregados y dejar que MarqueePanel redirija el evento al componente correcto.

En mi caso, se supone que estos componentes son enlaces.

  @Override public Component add(Component comp) { comp = super.add(comp); if(comp instanceof MouseListener) comp.removeMouseListener((MouseListener)comp); comp.addMouseListener(this); return comp; } 

Luego mapea el componente x a un MarqueePanel x y finalmente el componente correcto

 @Override public void mouseClicked(MouseEvent e) { Component source = (Component)e.getSource(); int x = source.getX() + e.getX(); int y = source.getY(); MarqueePanel2 marqueePanel = (MarqueePanel2) ((JComponent)e.getSource()).getParent(); double x2 = marqueePanel.getWidth(); double x1 = Math.abs(marqueePanel.scrollOffset); if(x >= x1 && x < = x2) { System.out.println("Bang " + x1); Component componentAt = getComponentAt(x+marqueePanel.scrollOffset, y); if(comp instanceof MouseListener) ((MouseListener) componentAt).mouseClicked(e); System.out.println(componentAt.getName()); } else { return; } //System.out.println(x); }