Java Swing 当中怎么实现鼠标悬停动态显示数据?

画夕颜 2020-01-03 08:43:08
最近自己写了一个 Swing 小程序,当中想实现一个功能:当鼠标悬停在 JLabel 控件 上的时候自动动态显示该控件中的详细文本内容。

试过了 setToolTipText() 这个方法,只能显示事先设置好的静态内容,但是我这个 JLabel 控件 内容是动态变化的,就算我添加一个循环也不行,必须要鼠标不断移动 setToolTipText() 才会更新显示内容!

大佬们,有没有什么办法可以解决啊???
...全文
1087 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
sunyiz 2020-01-05
  • 打赏
  • 举报
回复
刷新慢的问题,其实也可以解决的,只要设置一下ToolTip的最小刷新间隔就行
当tooltip浮动在 JFrame 范围内部时,非常平滑
如果 tooltip 的区域超出了 JFrame 的显示范围,可能会出现闪烁


public class Test {

private int num = 0;
private JLabel label = new JLabel("test", JLabel.CENTER);
private MouseEvent curMove;
private boolean entered;

public Test() {
JFrame frame = new JFrame();
label.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
// 记录上一次的鼠标移动位置
curMove = e;
}
});
label.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
// 标记鼠标在 JLabel 控件内
entered = true;
}

@Override
public void mouseExited(MouseEvent e) {
// 标记鼠标移出了JLabel 控件
entered = false;
}
});

// 创建刷新 ToolTip 的线程
initTipThread();


JPanel testPane = new JPanel(new BorderLayout());

testPane.add(new JButton("NORTH"), BorderLayout.NORTH);
testPane.add(new JButton("SOUTH"), BorderLayout.SOUTH);
testPane.add(new JButton("WEST"), BorderLayout.WEST);
testPane.add(new JButton("EAST"), BorderLayout.EAST);
testPane.add(label, BorderLayout.CENTER);

frame.setContentPane(testPane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

private void initTipThread() {
// 设置ToolTip的最小间隔刷新
ToolTipManager.sharedInstance().setInitialDelay(1);
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
// 延时(此时间不可小于ToolTip的最小间隔刷新,否则无法正常刷新)
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
num++;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
label.setToolTipText("" + num);
if (entered) {
// 当前鼠标在控件内时,定时模拟一次 mouseMoved 事件
ToolTipManager.sharedInstance().mouseMoved(curMove);
}
}
});
}
}
}).start();
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Test();
}
});

}

}
sunyiz 2020-01-04
  • 打赏
  • 举报
回复
用 tooltip 也是可以实现的,我给你写了个例子


import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;

public class Test {

private int num = 0;
private JLabel label = new JLabel("test");
private MouseEvent curMove;
private boolean entered;

public Test() {
JFrame frame = new JFrame();
label.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
// 记录上一次的鼠标移动位置
curMove = e;
}
});
label.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
// 标记鼠标在 JLabel 控件内
entered = true;
}

@Override
public void mouseExited(MouseEvent e) {
// 标记鼠标移出了JLabel 控件
entered = false;
}
});

// 创建刷新 ToolTip 的线程
initTipThread();

frame.setContentPane(label);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

private void initTipThread() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
// 延时(此时间不可太短,否则无法正常刷新)
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
num++;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
label.setToolTipText("" + num);
if (entered) {
// 当前鼠标在控件内时,定时模拟一次 mouseMoved 事件
ToolTipManager.sharedInstance().mouseMoved(curMove);
}
}
});
}
}
}).start();
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Test();
}
});

}

}
画夕颜 2020-01-04
  • 打赏
  • 举报
回复
引用 2 楼 三仙半 的回复:
我实际试验了一下,用setToolTipText()确实是不行,即使用线程刷新也不行,没去看tooltip的源码,估计设计时就是跟MouseMove关联的。然后,我就用TextField实现了一个,还是有缺陷的,你可以在这个基础上改。

import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;

public class AFrame extends JFrame {
private static final long serialVersionUID = 1L;

private JPanel contentPane;
private JLabel label;
private JTextField textField;

/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
AFrame frame = new AFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public AFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);

MouseAdapter adapter = new MouseAdapter() {
private boolean bRun;

@Override
public void mouseEntered(MouseEvent e) {
// 鼠标进入启动线程,在线程中监视label的内容变化,然后显示在textField上
bRun = true;
textField.setVisible(true);
new Thread(new Runnable() {
@Override
public void run() {
while (bRun) {
String c = label.getText();
int w = textField.getGraphics().getFontMetrics().stringWidth(c);
int h = textField.getHeight();
// 根据label的内容设置textField的尺寸。这里我直接用死值让它显示稍微正常了一些,应该还是可以优化的
textField.setSize(w + 10, h);
textField.setText(c);

try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}).start();
}

@Override
public void mouseMoved(MouseEvent e) {
// 在鼠标移动式,修改TextField的位置,使之追随鼠标
// 对TextField位置的控制也可以优化
textField.setLocation(label.getX() + e.getX() + 5, label.getY() + e.getY() + 15);
}

@Override
public void mouseExited(MouseEvent e) {
// 鼠标离开label,结束线程,隐藏textField
bRun = false;
textField.setVisible(false);
}
};

label = new JLabel("动态内容:");
label.addMouseListener(adapter);
label.addMouseMotionListener(adapter);
label.setBounds(81, 90, 72, 18);
contentPane.add(label);

textField = new JTextField();
textField.setEditable(false);
textField.setBounds(147, 138, 166, 24);
textField.setVisible(false);
contentPane.add(textField);
textField.setColumns(10);
new Thread(new Runnable() {
@Override
public void run() {
Random rnd = new Random();
int i;
while (true) {
i = rnd.nextInt(1000);
label.setText("随机整数:" + i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}).start();
}
}
大佬,我试了下,挺不错的!但是有一个问题,数据不变化的时候一切正常,一旦开始动态更新数据,这个悬浮窗就开始闪烁……是因为我没有加入休眠的原因吗?!
画夕颜 2020-01-04
  • 打赏
  • 举报
回复
引用 4 楼 sunyiz 的回复:
用 tooltip 也是可以实现的,我给你写了个例子


import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;

public class Test {

private int num = 0;
private JLabel label = new JLabel("test");
private MouseEvent curMove;
private boolean entered;

public Test() {
JFrame frame = new JFrame();
label.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
// 记录上一次的鼠标移动位置
curMove = e;
}
});
label.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
// 标记鼠标在 JLabel 控件内
entered = true;
}

@Override
public void mouseExited(MouseEvent e) {
// 标记鼠标移出了JLabel 控件
entered = false;
}
});

// 创建刷新 ToolTip 的线程
initTipThread();

frame.setContentPane(label);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

private void initTipThread() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
// 延时(此时间不可太短,否则无法正常刷新)
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
num++;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
label.setToolTipText("" + num);
if (entered) {
// 当前鼠标在控件内时,定时模拟一次 mouseMoved 事件
ToolTipManager.sharedInstance().mouseMoved(curMove);
}
}
});
}
}
}).start();
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Test();
}
});

}

}
谢谢,你这个也不错,就是有一点,数据刷新太慢了一点,不过我收藏了,说不定下次有用!
画夕颜 2020-01-03
  • 打赏
  • 举报
回复
引用 2 楼 三仙半 的回复:
我实际试验了一下,用setToolTipText()确实是不行,即使用线程刷新也不行,没去看tooltip的源码,估计设计时就是跟MouseMove关联的。然后,我就用TextField实现了一个,还是有缺陷的,你可以在这个基础上改。

import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;

public class AFrame extends JFrame {
private static final long serialVersionUID = 1L;

private JPanel contentPane;
private JLabel label;
private JTextField textField;

/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
AFrame frame = new AFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public AFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);

MouseAdapter adapter = new MouseAdapter() {
private boolean bRun;

@Override
public void mouseEntered(MouseEvent e) {
// 鼠标进入启动线程,在线程中监视label的内容变化,然后显示在textField上
bRun = true;
textField.setVisible(true);
new Thread(new Runnable() {
@Override
public void run() {
while (bRun) {
String c = label.getText();
int w = textField.getGraphics().getFontMetrics().stringWidth(c);
int h = textField.getHeight();
// 根据label的内容设置textField的尺寸。这里我直接用死值让它显示稍微正常了一些,应该还是可以优化的
textField.setSize(w + 10, h);
textField.setText(c);

try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}).start();
}

@Override
public void mouseMoved(MouseEvent e) {
// 在鼠标移动式,修改TextField的位置,使之追随鼠标
// 对TextField位置的控制也可以优化
textField.setLocation(label.getX() + e.getX() + 5, label.getY() + e.getY() + 15);
}

@Override
public void mouseExited(MouseEvent e) {
// 鼠标离开label,结束线程,隐藏textField
bRun = false;
textField.setVisible(false);
}
};

label = new JLabel("动态内容:");
label.addMouseListener(adapter);
label.addMouseMotionListener(adapter);
label.setBounds(81, 90, 72, 18);
contentPane.add(label);

textField = new JTextField();
textField.setEditable(false);
textField.setBounds(147, 138, 166, 24);
textField.setVisible(false);
contentPane.add(textField);
textField.setColumns(10);
new Thread(new Runnable() {
@Override
public void run() {
Random rnd = new Random();
int i;
while (true) {
i = rnd.nextInt(1000);
label.setText("随机整数:" + i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}).start();
}
}
这个参考不错,明天我试试。谢谢!
三仙半 2020-01-03
  • 打赏
  • 举报
回复
我实际试验了一下,用setToolTipText()确实是不行,即使用线程刷新也不行,没去看tooltip的源码,估计设计时就是跟MouseMove关联的。然后,我就用TextField实现了一个,还是有缺陷的,你可以在这个基础上改。

import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;

public class AFrame extends JFrame {
private static final long serialVersionUID = 1L;

private JPanel contentPane;
private JLabel label;
private JTextField textField;

/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
AFrame frame = new AFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public AFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);

MouseAdapter adapter = new MouseAdapter() {
private boolean bRun;

@Override
public void mouseEntered(MouseEvent e) {
// 鼠标进入启动线程,在线程中监视label的内容变化,然后显示在textField上
bRun = true;
textField.setVisible(true);
new Thread(new Runnable() {
@Override
public void run() {
while (bRun) {
String c = label.getText();
int w = textField.getGraphics().getFontMetrics().stringWidth(c);
int h = textField.getHeight();
// 根据label的内容设置textField的尺寸。这里我直接用死值让它显示稍微正常了一些,应该还是可以优化的
textField.setSize(w + 10, h);
textField.setText(c);

try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}).start();
}

@Override
public void mouseMoved(MouseEvent e) {
// 在鼠标移动式,修改TextField的位置,使之追随鼠标
// 对TextField位置的控制也可以优化
textField.setLocation(label.getX() + e.getX() + 5, label.getY() + e.getY() + 15);
}

@Override
public void mouseExited(MouseEvent e) {
// 鼠标离开label,结束线程,隐藏textField
bRun = false;
textField.setVisible(false);
}
};

label = new JLabel("动态内容:");
label.addMouseListener(adapter);
label.addMouseMotionListener(adapter);
label.setBounds(81, 90, 72, 18);
contentPane.add(label);

textField = new JTextField();
textField.setEditable(false);
textField.setBounds(147, 138, 166, 24);
textField.setVisible(false);
contentPane.add(textField);
textField.setColumns(10);
new Thread(new Runnable() {
@Override
public void run() {
Random rnd = new Random();
int i;
while (true) {
i = rnd.nextInt(1000);
label.setText("随机整数:" + i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}).start();
}
}

三仙半 2020-01-03
  • 打赏
  • 举报
回复
没太明白你所说的JLabel的内容是动态的是啥意思,有一个其他线程或者循环在不断更新它?鼠标停止着不动,它的内容一直在变,是吗?如果是这样的话,那就再开一个线程用于刷新。

62,628

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧