4

我已经构建了一个只显示一行的自定义组件。这条线是从左上角到右下角绘制的,作为 Paint 方法中的 Line2D。背景是透明的。我扩展了 JComponent。当鼠标指针位于最大位置时,这些线组件是可拖动的并改变它们的线颜色。距离画线 15 个像素。但是,如果我将多个这些组件添加到另一个扩展 JPanel 的自定义组件中,它们有时会重叠。我想实现,如果鼠标指针距离线超过 15 像素,则鼠标事件应该穿过组件。如何让它通过是我的问题。这甚至可能吗?

提前致谢!

4

5 回答 5

3

我想实现,如果鼠标指针距离线超过 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);
         }
    }
}
于 2011-08-30T13:56:07.390 回答
2

对于我在大学的最后一年项目,我做了一个白板程序并且遇到了同样的问题。对于用户在板上绘制的每个形状,我创建了一个 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 并使用它进行绘制的情况。

于 2011-08-30T14:58:29.490 回答
2

自从我接触 Swing 以来已经有一段时间了,但我认为您需要在父组件中处理鼠标事件,然后用线条遍历子组件并确定其中哪个应该处理事件(好吧,决定的逻辑仍应保留在 line 组件中,但 parent 将显式调用该逻辑,直到其中一个组件接受事件)。

于 2011-08-30T14:06:56.697 回答
1

我相信最简单的方法是捕捉事件并调用parent.processEvent(). 因此,您的组件将对事件透明,因为它会将它们传播给父级。

于 2011-08-30T13:32:37.710 回答
0

我一直在努力解决这类问题,并与父母和玻璃板一起尝试了所有的东西,直到我意识到contains方法的覆盖正是你想要的。因为当父母触发某种getcomponent你的“线”时,它会回复它:“不,不是我,我不在那里!” 并且循环将检查其他组件。此外,当您需要为可拖动对象设置复杂深度时,可以使用 JLayeredPane 后代。

于 2013-04-12T11:12:03.527 回答