我已经构建了一个只显示一行的自定义组件。这条线是从左上角到右下角绘制的,作为 Paint 方法中的 Line2D。背景是透明的。我扩展了 JComponent。当鼠标指针位于最大位置时,这些线组件是可拖动的并改变它们的线颜色。距离画线 15 个像素。但是,如果我将多个这些组件添加到另一个扩展 JPanel 的自定义组件中,它们有时会重叠。我想实现,如果鼠标指针距离线超过 15 像素,则鼠标事件应该穿过组件。如何让它通过是我的问题。这甚至可能吗?
提前致谢!
我已经构建了一个只显示一行的自定义组件。这条线是从左上角到右下角绘制的,作为 Paint 方法中的 Line2D。背景是透明的。我扩展了 JComponent。当鼠标指针位于最大位置时,这些线组件是可拖动的并改变它们的线颜色。距离画线 15 个像素。但是,如果我将多个这些组件添加到另一个扩展 JPanel 的自定义组件中,它们有时会重叠。我想实现,如果鼠标指针距离线超过 15 像素,则鼠标事件应该穿过组件。如何让它通过是我的问题。这甚至可能吗?
提前致谢!
我想实现,如果鼠标指针距离线超过 15 像素,则鼠标事件应该穿过组件。
如果您的子组件有一个鼠标侦听器,那么它将拦截在它上面发生的每个鼠标事件。如果要将 MouseEvent 转发到父组件,则应手动执行。例如,您可以实现扩展 MouseAdapter 的自定义鼠标侦听器:
public class yourMouseListener extends MouseAdapter{
//this will be called when mouse is pressed on the component
public void mousePressed(MouseEvent me) {
if (/*do your controls to decide if you want to propagate the event*/){
Component child = me.getComponent();
Component parent = child.getParent();
//transform the mouse coordinate to be relative to the parent component:
int deltax = child.getX() + me.getX();
int deltay = child.getY() + me.getY();
//build new mouse event:
MouseEvent parentMouseEvent =new MouseEvent(parent, MouseEvent.MOUSE_PRESSED, me.getWhen(), me.getModifiers(),deltax, deltay, me.getClickCount(), false)
//dispatch it to the parent component
parent.dispatchEvent( parentMouseEvent);
}
}
}
对于我在大学的最后一年项目,我做了一个白板程序并且遇到了同样的问题。对于用户在板上绘制的每个形状,我创建了一个 JComponent,这在他们绘制矩形时很好,但使用自由格式线工具更难。
我最终修复它的方法是完全取消 JComponents。我有一个 JPanel,其中包含自定义 Shape 对象的 Vector(我认为)。每个对象都有自己的坐标和线条粗细等。当用户点击棋盘时,JPanel 上的鼠标侦听器触发并遍历每个 Shape,并在每个 Shape 上调用 contains(int x, int y) 方法(x 和 y 是事件的坐标)。因为 Shapes 是在绘制时添加到 Vector 中的,所以我知道最后一个返回 true 的是最上面的 Shape。
这就是我用于直线包含的方法。数学可能有点不确定,但它对我有用。
public boolean contains(int x, int y) {
// Check if line is a point
if(posX == endX && posY == endY){
if(Math.abs(posY - y) <= lineThickness / 2 && Math.abs(posX - x) <= lineThickness / 2)
return true;
else
return false;
}
int x1, x2, y1, y2;
if(posX < endX){
x1 = posX;
y1 = posY;
x2 = endX;
y2 = endY;
}
else{
x1 = endX;
y1 = endY;
x2 = posX;
y2 = posY;
}
/**** USING MATRIX TRANSFORMATIONS ****/
double r_numerator = (x-x1)*(x2-x1) + (y-y1)*(y2-y1);
double r_denomenator = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
double r = r_numerator / r_denomenator;
// s is the position of the perpendicular projection of the point along
// the line: s < 0 = point is left of the line; s > 0 = point is right of
// the line; s = 0 = the point is along the line
double s = ((y1-y)*(x2-x1)-(x1-x)*(y2-y1) ) / r_denomenator;
double distance = Math.abs(s)*Math.sqrt(r_denomenator);
// Point is along the length of the line
if ( (r >= 0) && (r <= 1) )
{
if(Math.abs(distance) <= lineThickness / 2){
return true;
}
else
return false;
}
// else point is at one end of the line
else{
double dist1 = (x-x1)*(x-x1) + (y-y1)*(y-y1); // distance to start of line
double dist2 = (x-x2)*(x-x2) + (y-y2)*(y-y2); // distance to end of line
if (dist1 < dist2){
distance = Math.sqrt(dist1);
}
else{
distance = Math.sqrt(dist2);
}
if(distance <= lineThickness / 2){
return true;
}
else
return false;
}
/**** END USING MATRIX TRANSFORMATIONS****/
}
posX 和 posY 构成了行首的坐标,而 endX 和 endY 是行尾。如果单击位于线条中心的 lineThickness/2 范围内,则返回 true,否则您必须沿着线条的正中间单击。
绘制 Shapes 是将 JPanel 的 Graphics 对象传递给每个 Shape 并使用它进行绘制的情况。
自从我接触 Swing 以来已经有一段时间了,但我认为您需要在父组件中处理鼠标事件,然后用线条遍历子组件并确定其中哪个应该处理事件(好吧,决定的逻辑仍应保留在 line 组件中,但 parent 将显式调用该逻辑,直到其中一个组件接受事件)。
我相信最简单的方法是捕捉事件并调用parent.processEvent()
. 因此,您的组件将对事件透明,因为它会将它们传播给父级。
我一直在努力解决这类问题,并与父母和玻璃板一起尝试了所有的东西,直到我意识到contains
方法的覆盖正是你想要的。因为当父母触发某种getcomponent
你的“线”时,它会回复它:“不,不是我,我不在那里!” 并且循环将检查其他组件。此外,当您需要为可拖动对象设置复杂深度时,可以使用 JLayeredPane 后代。