我在Java中遇到了一个问题,我使用的是我创建的“画布”类,它是一个扩展的JPanel,用于绘制动画环形图.此图表使用MouseListener来获取点击事件.
问题是鼠标位置似乎不准确,这意味着它似乎不是相对于“画布”而是相对于窗口(在左上角,我得到约30px的y coord).
这是我的代码:
我创建了一个类,它扩展了JPanel并且有一个BufferedImage作为成员.
public class Canvas extends JPanel {
public BufferedImage buf;
private RingChart _parent;
public Canvas(int width, int height, RingChart parent){
buf = new BufferedImage(width, height, 1);
...
在paint组件方法中,我只绘制缓冲图像,因此我可以通过在缓冲图像上绘制来从“外部”绘制画布,这是公共的.
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(buf, null, 0, 0);
}
现在有一个类RingChart包含一个“画布”:
public class RingChart extends JFrame{
public Canvas c;
...
我从canvas类的bufferedImage创建了一个Graphics2D.这个g2d用于绘画:
public RingChart(){
c = new Canvas(1500,980,this);
add(c);
setSize(1500, 1000);
setTitle("Hans");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
g2d = (Graphics2D)c.buf.createGraphics();
...
我现在想要实现的是一个鼠标监听器,它监听画布上发生的鼠标事件.因此,当用户点击画布时,我可以通过事件变量检索他在画布上单击的位置.
所以我创建了一个鼠标监听器:
class MouseHandler implements MouseListener {
@Override
public void mouseClicked(MouseEvent e){
RingChart r = ((Canvas)e.getSource()).getParent();
r.mouseClick(e);
}
...
…并将此鼠标侦听器添加到RingChart类的画布中(myChart是RingChart的一个实例,c是它包含的画布):
...
MouseHandler mouse = new MouseHandler();
myChart.c.addMouseListener(mouse);
...
但正如我上面提到的,调用click事件时返回的鼠标位置似乎不准确.我认为错误必须以某种方式创建mouseListener,或者将其分配给错误的元素或类似的东西.但我已经尝试了很多东西而且没有改变.也许有人能告诉我,我做错了什么?
更新:
函数“mouseClick”的代码,它是RingChart的成员并在鼠标侦听器中调用:
public void mouseClick(MouseEvent evt){
//evt = SwingUtilities.convertMouseEvent(this, evt, c);
if(evt.getButton() == MouseEvent.BUTTON1 && animation == null){
for(Element e : elements){
if(e.getShape() != null && e.getShape().contains(evt.getPoint())){
//do some stuff
}
}
}
}
同样,我的类的层次结构:
RingChart – 包含a – >画布 – got a – >的MouseListener.
此函数中的形状是在画布上绘制的形状.现在我想检查,如果用户点击了其中一个.所以我认为,形状应该是画布坐标,事件位置应该是画布坐标,一切都应该放在一起.但事实并非如此.
现在用户MadProgrammer告诉我,要使用ConvertMouseEvent函数.但我目前还没有看到我应该明智地使用哪种方式.
更新:
我找到了一个解决方案:我所要做的就是将画布不是直接添加到JFrame而是添加到JFrame的ContentPane:
所以与其:
public RingChart(){
c = new Canvas(1500,980,this);
add(c);
...
我做:
public RingChart(){
c = new Canvas(1500,980,this);
getContentPane().add(c);
...
然后我将MouseListener提供给ContentPane.
getContentPane().addMouseListener(new MouseHandler());
getContentPane().addMouseMotionListener(new MouseMoveHandler());
我不知道,如果这是一个优雅的解决方案,但它的工作原理.
解决方法:
鼠标事件自动转换为相对于它发生的组件,即点0x0始终是组件的左上角.
通过使用RingChart r =((Canvas)e.getSource()).getParent(),您已经有效地更改了引用,现在意味着该位置不再有效.
您需要转换位置,使其坐标位于父组件的上下文中.看看SwingUtilities.convertMouseEvent(Component, MouseEvent, Component)
更新与图片
让我们举个例子……
蓝色框与红色框的相对位置为50px x 50px.如果你点击蓝色框,让我们说在25×25,鼠标坐标将相对于蓝色框(0x0将是蓝色框的左上角).
如果然后将此事件传递给红色框并尝试使用其中的坐标,您会发现坐标现在位于红色框的左上角和蓝色框之间,因为坐标是上下文敏感的.
为了使它工作,您需要将鼠标事件位置从蓝色框转换为红色框,这将使其成为75×75
现在,当你将鼠标事件传递给RingChart时,我不知道你在做什么,所以我只是猜测这是你正面临的问题.
使用点击代码更新
好吧,比方说,你有一个100×100的画布.您以50×50点击该画布.然后,您将该值传递回链中.
public void mouseClick(MouseEvent evt){
//evt = SwingUtilities.convertMouseEvent(this, evt, c);
if(evt.getButton() == MouseEvent.BUTTON1 && animation == null){
for(Element e : elements){
// Here, we are asking the shape if it contains the point 50x50...
// Not 150x150 which would be the relative position of the click
// in the context to the RingChart, which is where all your objects
// are laid out.
// So even the original Canvas you clicked on will return
// false because it's position + size (100x100x width x height)
// does not contain the specified point of 50x50...
if(e.getShape() != null && e.getShape().contains(evt.getPoint())){
//do some stuff
}
}
}
}
更新
我认为你的参考方式错误…
public static MouseEvent convertMouseEvent(Component source,
MouseEvent sourceEvent,
Component destination)
我认为它应该读起来像
evt = SwingUtilities.convertMouseEvent(evt.getComponent(), evt, this);
使用代码示例更新
好的,所以,我把这个小例子放在一起……
public class TestMouseClickPoint extends JFrame {
private ContentPane content;
public TestMouseClickPoint() throws HeadlessException {
setSize(600, 600);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setLayout(new BorderLayout());
content = new ContentPane();
add(content);
}
protected void updateClickPoint(MouseEvent evt) {
content.updateClickPoint(evt);
}
protected class ContentPane extends JPanel {
private Point relativePoint;
private Point absolutePoint;
public ContentPane() {
setPreferredSize(new Dimension(600, 600));
setLayout(null); // For testing purpose only...
MousePane mousePane = new MousePane();
mousePane.setBounds(100, 100, 400, 400);
add(mousePane);
}
protected void updateClickPoint(MouseEvent evt) {
absolutePoint = new Point(evt.getPoint());
evt = SwingUtilities.convertMouseEvent(evt.getComponent(), evt, this);
relativePoint = new Point(evt.getPoint());
System.out.println(absolutePoint);
System.out.println(relativePoint);
repaint();
}
protected void paintCross(Graphics2D g2d, Point p) {
g2d.drawLine(p.x - 5, p.y - 5, p.x + 5, p.y + 5);
g2d.drawLine(p.x - 5, p.y + 5, p.x + 5, p.y - 5);
}
/*
* This is not recommended, but I want to paint ontop of everything...
*/
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
if (relativePoint != null) {
g2d.setColor(Color.BLACK);
paintCross(g2d, relativePoint);
}
if (absolutePoint != null) {
g2d.setColor(Color.RED);
paintCross(g2d, absolutePoint);
}
}
}
protected class MousePane extends JPanel {
private Point clickPoint;
public MousePane() {
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
clickPoint = e.getPoint();
TestMouseClickPoint.this.updateClickPoint(e);
repaint();
}
});
setBorder(new LineBorder(Color.RED));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLUE);
if (clickPoint != null) {
g2d.drawLine(clickPoint.x, clickPoint.y - 5, clickPoint.x, clickPoint.y + 5);
g2d.drawLine(clickPoint.x - 5, clickPoint.y, clickPoint.x + 5, clickPoint.y);
}
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
new TestMouseClickPoint().setVisible(true);
}
}
基本上,它将描绘三点.单击鼠标的点(相对于事件源),父容器中的未转换点以及父容器的转换点.
您需要做的下一件事是确定鼠标位置实际已被转换,失败了.我可能需要查看代码的工作示例,以确定您实际在做什么.