JavaSwingでマーキー効果を実装するにはどうすればよいですか
6 に答える
を使用した例を次に示しjavax.swing.Timer
ます。
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));
}
}
これが遅い回答であることは知っていますが、この回答の重複と見なされたために閉じられたマーキーに関する別の質問を見ました。
そこで、ここで提案されている他の回答とは異なるアプローチをとる提案を追加したいと思いました。
MarqueePanelは、テキストだけでなくパネル上のコンポーネントをスクロールします。したがって、これにより、Swingコンポーネントを最大限に活用できます。単純なマーキーは、テキスト付きのJLabelを追加することで使用できます。より洗練されたマーキーは、HTMLでJLabelを使用する場合があるため、テキストにさまざまなフォントと色を使用できます。画像を使用して2番目のコンポーネントを追加することもできます。
これが私があなたを始めるために一緒に投げたいくつかのコードです。私は通常、ActionListenerコードを取得して、このロジックをパネルから分離するためにある種のMarqueeController
クラスに配置しますが、MVCアーキテクチャの編成については別の質問であり、このような単純なクラスではそれほど重要ではない場合があります。
これを行うのに役立つさまざまなアニメーションライブラリもありますが、私は通常、このような1つの問題を解決するためだけにライブラリをプロジェクトに含めるのは好きではありません。
パブリッククラスMarqueePanelはJPanelを拡張します{ プライベートJLabeltextLabel; private int panelLocation; プライベートActionListenertaskPerformer; プライベートブールisRunning=false; public static final int FRAMES_PER_SECOND = 24; public static final int MOVEMENT_PER_FRAME = 5; / ** *クラスコンストラクターはマーキーパネルを作成します。 * / public MarqueePanel(){ this.setLayout(null); this.textLabel = new JLabel( "ここにテキストをスクロール"); this.panelLocation = 0; this.taskPerformer = new ActionListener(){ public void actionPerformed(ActionEvent evt){ MarqueePanel.this.tickAnimation(); } } } / ** *アニメーションを開始します。 * / public void start(){ this.isRunning = true; this.tickAnimation(); } / ** *アニメーションを停止します。 * / public void stop(){ this.isRunning = false; } / ** *ラベルを1フレーム左に移動します。表示範囲外の場合は元に戻します *右側、表示範囲外。 * / private void tickAnimation(){ this.panelLocation-= MarqueePanel.MOVEMENT_PER_FRAME; if(this.panelLocation <this.textLabel.getWidth()) this.panelLocaton = this.getWidth(); this.textLabel.setLocation(this.panelLocation、0); this.repaint(); if(this.isRunning){ タイマーt=new Timer(1000 / MarqueePanel.FRAMES_PER_SECOND、this.taskPerformer); t.setRepeats(false); t.start(); } } }
基本的な答えは、テキスト/グラフィックをビットマップに描画してから、ビットマップオフセットをある程度ペイントするコンポーネントを実装することです。通常、マーキー/ティッカーは左にスクロールするため、オフセットが増加します。これは、ビットマップが-offsetでペイントされることを意味します。コンポーネントは定期的に起動するタイマーを実行し、オフセットをインクリメントしてそれ自体を無効にして再描画します。
ラッピングのようなものは、処理するのが少し複雑ですが、かなり簡単です。オフセットがビットマップ幅を超える場合は、リセットして0に戻します。オフセット+コンポーネント幅>ビットマップ幅の場合、ビットマップの先頭から始まるコンポーネントの残りの部分をペイントします。
きちんとしたティッカーの鍵は、スクロールをできるだけスムーズでちらつきのないものにすることです。したがって、結果をダブルバッファリングすることを検討する必要がある場合があります。最初にスクロールビットをビットマップにペイントしてから、画面に直接ペイントするのではなく、一度にレンダリングします。
フレームまたはパネルにJLabelを追加します。
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();
}
}
これは@camickrMarqueePanelの改善であると思われます。上記をご覧ください。
MarqueePanelに追加された特定のコンポーネントにマウスイベントをマップするには
add(Component comp)
コンポーネントのすべてのマウスイベントを送信するためのMarqueePanelのオーバーライド
ここでの問題は、個々のコンポーネントから発生するMouseEventsをどうするかです。私のアプローチは、追加されたコンポーネントからマウスリスナーを削除し、MarqueePanelにイベントを正しいコンポーネントにリダイレクトさせることです。
私の場合、これらのコンポーネントはリンクであると想定されています。
@Override
public Component add(Component comp) {
comp = super.add(comp);
if(comp instanceof MouseListener)
comp.removeMouseListener((MouseListener)comp);
comp.addMouseListener(this);
return comp;
}
次に、コンポーネントxをMarqueePanel xにマップし、最後に正しいコンポーネントをマップします
@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);
}