首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Swing 實現跑馬燈標籤:多線程動畫與自定義繪製詳解

     編輯:Java 相關知識

效果預覽

一個繼承自 JLabel 的 MoveLabel 組件,文字在水平方向無限循環滾動,支持自定義速度、顏色、字體,可直接嵌入 Swing 界面作為通告欄、狀態提示等。

核心設計思路

  1. 繼承 JLabel 實現 Runnable:複用標籤佈局/字體/顏色屬性,自帶線程驅動動畫
  2. 重寫 paintComponent(Graphics g):自定義繪製邏輯,計算文字 X 座標實現位移
  3. 後台線程循環 repaint():每 50ms 更新位移並觸發重繪,Thread.sleep 控制幀率

完整代碼實現

import javax.swing.*;
import java.awt.*;

public class MarqueeLabelDemo extends JFrame {
    public MarqueeLabelDemo() {
        super("跑馬燈標籤演示");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(400, 150);
        setLocationRelativeTo(null);
        setLayout(new BorderLayout());

        MoveLabel label = new MoveLabel("歡迎使用 Java Swing 跑馬燈組件 - 支持自定義速度、顏色、字體...");
        label.setFont(new Font("微軟雅黑", Font.BOLD, 18));
        label.setForeground(Color.WHITE);
        label.setBackground(new Color(0x2C3E50));
        label.setOpaque(true);
        label.setPreferredSize(new Dimension(380, 50));

        add(label, BorderLayout.CENTER);
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(MarqueeLabelDemo::new);
    }

    /** 跑馬燈標籤核心類 */
    static class MoveLabel extends JLabel implements Runnable {
        private final Thread animator;
        private int x = 0;           // 當前文字 X 偏移
        private int textWidth = 0;   // 文字像素寬度

        public MoveLabel(String text) {
            super(text);
            this.animator = new Thread(this);
            this.animator.setDaemon(true); // 隨 JVM 退出
            this.animator.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);

            // 計算文字寬度(首次/字體變更時)
            FontMetrics fm = g2.getFontMetrics(getFont());
            textWidth = fm.stringWidth(getText());
            int h = getHeight();
            int baseline = (h - fm.getHeight()) / 2 + fm.getAscent();

            // 背景填充
            g2.setColor(getBackground());
            g2.fillRect(0, 0, getWidth(), h);

            // 繪製主文字
            g2.setColor(getForeground());
            g2.drawString(getText(), x, baseline);

            // 繪製銜接副本(無縫循環關鍵)
            if (x + textWidth < getWidth()) {
                g2.drawString(getText(), x + textWidth + 10, baseline); // 10px 間隙
            }
        }

        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                x -= 2; // 每幀左移 2px,速度可通過 setSpeed(int px) 控制
                if (x < -textWidth - 10) { // 完全移出後重置
                    x = getWidth();
                }
                repaint(); // 觸發 EDT 重繪
                try {
                    Thread.sleep(50); // ~20 FPS
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // 保留中斷狀態
                    break;
                }
            }
        }

        // 擴展:動態調速
        public void setSpeed(int pixelsPerFrame) {
            // 可通過修改 run() 中的 x -= speed 實現,此處略
        }
    }
}

關鍵技術點深度解析

1. 雙重繪製實現無縫循環

單次繪製無法實現「文字剛移出屏幕右側,左側立即出現」的效果。核心邏輯:

// 主文字位置 x
g.drawString(text, x, y);
// 副本文字位置 x + textWidth + gap
if (x + textWidth < getWidth()) {
    g.drawString(text, x + textWidth + gap, y);
}

當主文字尚未完全移出,副本已從右側進入,視覺上形成連續流動。

2. 線程模型:後台計算 + EDT 繪製

  • 動畫線程:計算座標、睡眠控制幀率,**不直接操作 UI**
  • 事件分發線程 (EDT):repaint() 將繪製任務投遞到 EDT 隊列,由 paintComponent 執行實際繪製
  • 符合 Swing 單線程規則,避免併發繪製導致閃爍/異常

3. 性能優化細節

  • FontMetrics 緩存:文字寬度僅在字體/內容變更時重新計算
  • 開啟抗鋸齒:RenderingHints.KEY_TEXT_ANTIALIASING 提升視覺質量
  • 幀率 20 FPS(50ms/幀)平衡流暢度與 CPU 佔用
  • 設置 setDaemon(true) 避免主窗口關閉後動畫線程阻塞 JVM 退出

功能擴展建議

功能實現方案
垂直滾動修改 y 座標,paintComponent 中計算行高
顏色漸變g2.setPaint(new GradientPaint(...)) 繪製文字
滾動暫停/恢復增加 volatile boolean paused 标志,run() 中 wait/notify
富文本/圖片混排改用 JEditorPane + HTML,或自定義 Icon 實現
多行跑馬燈維護 List<String> lines,逐行繪製,行間距固定

常見問題排查

  • 文字不顯示:檢查 setOpaque(true) + 背景色對比度,或 getWidth() 為 0(布局未完成),可在 addComponentListener 中延遲啟動動畫
  • 閃爍嚴重:確保雙緩衝(Swing 默認開啟),避免在 paintComponent 外直接調用 getGraphics()
  • 中文寬度計算不準:FontMetrics.stringWidth() 已正確支持雙字節字符,無需特殊處理

總結

本文實現的 MoveLabel 組件,通過 繼承 JLabel + 實現 Runnable + 重寫 paintComponent + 雙重繪製 四步,打造出輕量級、可復用、視覺流暢的跑馬燈效果。代碼結構清晰,擴展性強,可直接作為項目通用組件庫收錄。掌握「後台線程計算座標 + EDT 繪製」這一 Swing 動畫核心模式,舉一反三可實現進度條動畫、粒子特效、圖表動態渲染等豐富視覺效果。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved