如何在Java swing中构建点击组件?

发布于 2024-12-01 23:56:54 字数 248 浏览 0 评论 0原文

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

提前致谢!

I have built a custom component that shows only a line. The line is drawn from the top left corner to the bottom right corner as a Line2D at the paint method. The background is transparent. I extended JComponent. These line components are draggable and change their line color when the mouse pointer is located max. 15 pixels away from the drawn line.
But if I have multiple of these components added to another custom component that extends JPanel they sometimes overlap. I want to implement that if the mouse pointer is more than 15 pixels away from the line the mouse events should fall through the component. How to let it fall through is my problem.
Is that even possible?

Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

冷心人i 2024-12-08 23:56:54

我想实现如果鼠标指针超过 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);
         }
    }
}

I want to implement that if the mouse pointer is more than 15 pixels
away from the line the mouse events should fall through the component.

If your child component has a mouse listener, then it will intercept every mouse event occurring over it. If you want to forward the MouseEvent to the parent Component you should manually do it. For example you can implement your custom mouse listener extending 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);
         }
    }
}
烧了回忆取暖 2024-12-08 23:56:54

在大学最后一年的项目中,我做了一个白板程序并遇到了同样的问题。对于用户在板上绘制的每个形状,我创建了一个 JComponent,这在绘制矩形时很好,但使用自由形状线条工具会更困难。

我最终解决这个问题的方法是完全取消 JComponent。我有一个 JPanel,其中包含一个自定义 Shape 对象的 Vector(我认为)。每个对象都有自己的坐标和线条粗细等。当用户单击面板时,JPanel 上的鼠标侦听器会触发并遍历每个 Shape,对每个 Shape 调用 contains(int x, int y) 方法(x 和 y 是事件的坐标)。因为形状是在绘制时添加到向量中的,所以我知道最后一个返回 true 的是最上面的形状。

这就是我用于直线包含方法的方法。数学可能有点不确定,但它对我有用。

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,否则您必须沿着线的中间向右单击。

绘制形状是将 JPanel 的 Graphics 对象传递给每个形状并用它进行绘制的情况。

For my final year project at university I did a whiteboard program and had the same problem. For each shape the user drew on the board I created a JComponent, which was fine when they were drawing rectangles, but more difficult with the free form line tool.

The way I fixed it in the end was to do away with JComponents altogether. I had a JPanel which held a Vector (I think) of custom Shape objects. Each object held its own coordinates and line thicknesses and such. When the user clicked on the board, the mouse listener on the JPanel fired and went through each Shape calling a contains(int x, int y) method on each one (x and y being the coordinates of the event). Because the Shapes were added to the Vector as they were drawn I knew that the last one to return true was the topmost Shape.

This is what I used for a straight line contains method. The maths might be a bit iffy but it worked for me.

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 and posY make up the coordinates of the start of the line and endX and endY are, yep, the end of the line. This returned true if the click is within lineThickness/2 of the centre of the line, otherwise you have to click right along the very middle of the line.

Drawing the Shapes was a case of passing in the JPanel's Graphics object to each Shape and doing the drawing with that.

百合的盛世恋 2024-12-08 23:56:54

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

It's been a while since I touched Swing, but I think you will need to handle your mouse events in the parent component and then loop through the child components with lines and determine which one of them should handle the event (well, the logic of deciding should still remain in the line component, but parent will explicitly invoke that logic until one of the components takes the event).

烙印 2024-12-08 23:56:54

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

I believe that the easiest way is to catch the event and call parent.processEvent(). So, you component will be transparent for events because it will propagate them to parent.

回忆那么伤 2024-12-08 23:56:54

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

I was struggling with this sort of question and tried all the stuff with parents and glasspane until i realised that override of contains method does just what you want. Because when parent fires some sort of getcomponent your 'Line' will reply to it: 'no, its not me, i'm not there!' and the loop will check other components.
Also, when you need to setup a complex depth to your draggable object, you can use JLayeredPane descendant.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文