Java Swing中的字幕效应
如何在Java Swing中实现选框效果基本答案是将文本/图形绘制成位图,然后实现一个组件,将位图偏移一定量。通常选框/标记向左滚动,因此偏移量增加,这意味着位图以-offset绘制。组件运行一个定期触发的计时器,增加偏移量并使其自身失效,以便重新绘制 包装之类的事情处理起来稍微复杂一些,但相当简单。如果偏移量超过位图宽度,则将其重置回0。如果偏移+组件宽度>位图宽度,则从位图的开头开始绘制组件的其余部分Java Swing中的字幕效应,java,swing,marquee,Java,Swing,Marquee,如何在Java Swing中实现选框效果基本答案是将文本/图形绘制成位图,然后实现一个组件,将位图偏移一定量。通常选框/标记向左滚动,因此偏移量增加,这意味着位图以-offset绘制。组件运行一个定期触发的计时器,增加偏移量并使其自身失效,以便重新绘制 包装之类的事情处理起来稍微复杂一些,但相当简单。如果偏移量超过位图宽度,则将其重置回0。如果偏移+组件宽度>位图宽度,则从位图的开头开始绘制组件的其余部分 一个像样的股票代码的关键是使滚动尽可能平滑和不闪烁。因此,有必要考虑双重缓冲的结果,首先将
一个像样的股票代码的关键是使滚动尽可能平滑和不闪烁。因此,有必要考虑双重缓冲的结果,首先将滚动位绘制成位图,然后将其绘制成一行而不是直接画到屏幕上。 < p>这里是一些我一起启动的代码。我通常会将ActionListener代码放在某种
MarqueeController
类中,以将此逻辑与面板分开,但这是关于组织MVC架构的另一个问题,在这样一个足够简单的类中,它可能没有那么重要
还有各种各样的动画库可以帮助您做到这一点,但我通常不喜欢将库包含到项目中,而只是为了解决这样一个问题
public class MarqueePanel extends JPanel {
private JLabel textLabel;
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;
/**
* Class constructor creates a marquee panel.
*/
public MarqueePanel() {
this.setLayout(null);
this.textLabel = new JLabel("Scrolling Text Here");
this.panelLocation = 0;
this.taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
MarqueePanel.this.tickAnimation();
}
}
}
/**
* Starts the animation.
*/
public void start() {
this.isRunning = true;
this.tickAnimation();
}
/**
* Stops the animation.
*/
public void stop() {
this.isRunning = false;
}
/**
* Moves the label one frame to the left. If it's out of display range, move it back
* to the right, out of display range.
*/
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) {
Timer t = new Timer(1000 / MarqueePanel.FRAMES_PER_SECOND, this.taskPerformer);
t.setRepeats(false);
t.start();
}
}
}
公共类MarquePanel扩展了JPanel{
专用JLabel文本标签;
私人场所;
私人行动执行者;
私有布尔值isRunning=false;
公共静态最终整数帧每秒=24;
公共静态最终整数移动每帧=5;
/**
*类构造函数创建一个选框面板。
*/
公共Marquepanel(){
此.setLayout(null);
this.textLabel=newjlabel(“此处滚动文本”);
this.panelLocation=0;
this.taskPerformer=new ActionListener(){
已执行的公共无效操作(操作事件evt){
MarquePanel.this.tickAnimation();
}
}
}
/**
*启动动画。
*/
公开作废开始(){
this.isRunning=true;
这个.tickAnimation();
}
/**
*停止动画。
*/
公共停车场(){
this.isRunning=false;
}
/**
*将标签向左移动一帧。如果超出显示范围,请将其向后移动
*右侧,超出显示范围。
*/
私有动画(){
this.panelLocation-=每帧MarquePanel.MOVEMENT\u;
if(this.panelLocationjavax.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));
}
}
导入java.awt.EventQueue;
导入java.awt.Font;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入javax.swing.JFrame;
导入javax.swing.JLabel;
导入javax.swing.JPanel;
导入javax.swing.Timer;
/**@见http://stackoverflow.com/questions/3617326 */
公共类选框测试{
专用void display(){
JFrame f=新JFrame(“MarqueeTest”);
f、 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String s=“明天,明天,明天,”
+“每天都以这种小节奏爬行”
+“到记录时间的最后一个音节;……”
+这是一个白痴讲的故事,充满了
+“喧哗和愤怒毫无意义。”;
Marquepanel mp=新的Marquepanel(s,32);
f、 添加(mp);
f、 包装();
f、 setLocationRelativeTo(空);
f、 setVisible(真);
mp.start();
}
公共静态void main(字符串[]args){
invokeLater(新的Runnable(){
@凌驾
公开募捐{
新建MarqueTest().display();
}
});
}
}
/**侧滚n个字符的s*/
类MarquePanel扩展了JPanel实现ActionListener{
私人静态最终积分率=12;
专用最终定时器=新定时器(1000/速率,此值);
专用最终JLabel标签=新JLabel();
私有最终字符串s;
私人终审法院;
私有整数索引;
公共MarquePanel(字符串s,整数n){
如果(s==null | | n<1){
抛出新的IllegalArgumentException(“空字符串或n<1”);
}
StringBuilder sb=新的StringBuilder(n);
对于(int i=0;is.长度()-n){
指数=0;
}
setText(s.substring(index,index+n));
}
}
我知道这是一个迟来的答案,但我刚刚看到另一个问题,关于一个被关闭的字幕,因为它被认为是这个答案的重复
因此,我想我应该添加我的建议,它采用了一种不同于这里建议的其他答案的方法
滚动面板上的组件,而不仅仅是文本。因此,这允许您充分利用任何Swing组件。通过添加带有文本的JLabel,可以使用简单的选框。更喜欢的字幕可能会将JLabel与HTML一起使用,这样您就可以对文本使用不同的字体和颜色。您甚至可以添加带有图像的第二个组件。将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();
}
}
这应该是个小鬼
@Override
public Component add(Component comp) {
comp = super.add(comp);
if(comp instanceof MouseListener)
comp.removeMouseListener((MouseListener)comp);
comp.addMouseListener(this);
return comp;
}
@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);
}