Java Swing中的选取框效果

我如何在Java Swing中实现选取框效果?

这是一个使用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)); } } 

我知道这是一个迟到的答案,但是我刚刚看到另一个关于选取框的问题,因为它被认为是这个答案的重复。

所以我想我会添加我的build议,采取不同于这里build议的其他答案的方法。

MarqueePanel在面板上滚动组件不仅仅是文本。 所以这可以让你充分利用任何Swing组件。 通过添加带有文本的JLabel可以使用简单的选框。 一个fancier选框可能会使用带有HTML的JLabel,因此您可以为文本使用不同的字体和颜色。 你甚至可以添加一个图像的第二个组件。

一个java swing程序来选取文本

我刚刚Google,find了这个链接 。 我运行的代码,它似乎做你想要的。

基本的答案是你将你的文本/graphics绘制成位图,然后实现一个组件来绘制位图偏移量。 通常marquees / tickers向左滚动,所以偏移量增加,这意味着位图被绘制在-offset上。 你的组件运行一个定时器,定期触发,增加偏移量,并使其自身失效,以便重新绘制。

像包装这样的事情处理起来有点复杂,但相当简单。 如果偏移量超过位图宽度,则将其重置为0.如果偏移量+组件宽度>位图宽度,则从位图开始处绘制组件的其余部分。

一个像样的股票的关键是使滚动尽可能平滑和无闪烁。 因此,可能需要考虑双缓冲结果,首先将滚动位绘制成位图,然后一次渲染,而不是直接绘制到屏幕上。

这里有一些代码,我扔在一起,让你开始。 我通常会采取ActionListener代码,并把它放在某种types的MarqueeController类中,以保持这个逻辑与面板分开,但这是关于组织MVC架构的一个不同的问题,在这样一个简单的类中,它可能不是那么重要。

也有各种animation库可以帮助你做到这一点,但我通常不喜欢把图书馆纳入项目只是为了解决这样的问题。

公共类MarqueePanel扩展JPanel {
  私人JLabel textLabel;
   private int panelLocation;
  私人的ActionListener的taskPerformer;
   private boolean isRunning = false;

  公共静态最终诠释FRAMES_PER_SECOND = 24;
  公共静态最终诠释MOVEMENT_PER_FRAME = 5;

   / **
    *类构造函数创build一个选取框面板。
    * /

   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();
       }
     }
   }

   / **
    *开始animation。
    * /

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

   / **
    *停止animation。
    * /

   public void stop(){
     this.isRunning = false;
   }

   / **
    *将标签向左移动一帧。 如果超出显示范围,请将其移回
    *在右边,超出显示范围。
    * /

   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 =新定时器(1000 / MarqueePanel.FRAMES_PER_SECOND,this.taskPerformer);
       t.setRepeats(假);
       t.start();
     }
   }
 }

将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(); } } 

这应该是@camickr MarqueePanel的改进。 请参阅上文。

将鼠标事件映射到添加到MarqueePanel的特定组件

覆盖MarqueePanel的add(Component comp) ,以便引导组件的所有鼠标事件

这里的一个问题是从单个组件触发的MouseEvent如何处理。 我的方法是从添加的组件中删除鼠标侦听器,并让MarqueePanel将事件redirect到正确的组件。

在我的情况下,这些组件应该是链接。

  @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); }