2

我刚开始为我自己使用一个记录工具,它可以记录健身房/跑步的统计数据,我在 swing/awt 方面的唯一经验是你可以完全控制 Graphics2D 对象的游戏的主动渲染,而不是依赖于实现带有覆盖油漆的摆动组件。

无论如何,我希望创建一个虚拟 JComponent,我可以将它添加到我的一个面板(此面板将显示图形、统计信息等,具体取决于我从另一个带有选项的不同侧面板中选择的内容),除了监听鼠标事件前面提到的面板并在鼠标拖动上绘制一个选择矩形,以便在存在更高分辨率时可以放大数据。我只是不知道如何,我已将组件添加到面板中,但它在面板内没有注册任何内容,而是似乎有一个仅限于面板/框架底部的本地空间。

这是组件

package gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.JComponent;

@SuppressWarnings("serial")
public class MarkerRectangle extends JComponent implements MouseListener,
        MouseMotionListener {

private boolean draw;
private int startX, endX, startY, endY;
private Color color = new Color(0, 255, 0, 100);

public MarkerRectangle(int width, int height) {
    setPreferredSize(new Dimension(width, height));
    this.addMouseListener(this);
    this.addMouseMotionListener(this);
}

@Override
public void mouseClicked(MouseEvent e) {
    System.out.println("Mouse clicked@ " + e.getX() + "," + e.getY());
}

@Override
public void mouseEntered(MouseEvent e) {
    System.out.println("Mouse entered @ " + e.getX() + "," + e.getY());

}

@Override
public void mouseExited(MouseEvent e) {
    System.out.println("Mouse left @ " + e.getX() + "," + e.getY());
}

@Override
public void mousePressed(MouseEvent e) {
    System.out.println("Mouse pressed@ " + e.getX() + "," + e.getY());
}

@Override
public void mouseReleased(MouseEvent e) {
    System.out.println("Mouse was released @ " + e.getX() + "," + e.getY());
}

@Override
public void mouseDragged(MouseEvent e) {
    System.out.println("Mouse being dragged, currently@ " + e.getX() + ","
            + e.getY());

}

@Override
public void mouseMoved(MouseEvent e) {
    System.out.println("I registered a move@ " + e.getX() + "," + e.getY());
}

@Override
// Draw rectangle
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();
    g2d.dispose();
}

}

面板_

public class GraphPane extends JPanel {


public GraphPane(Graph graph) {
    this.add(graph);
    this.add(new MarkerRectangle(800, 600));
    this.setBackground(Color.gray);
    this.setPreferredSize(new Dimension(800, 600));
}


}

,保存随机数据atm

public class Graph extends JComponent {
private GeneralPath data;
private Stroke stroke;

public Graph() {
    this.data = new GeneralPath();
    this.stroke = new BasicStroke(3f);
    this.setPreferredSize(new Dimension(750, 550));
    init();
}

private void init() {
    data.moveTo(0, 0);
    double cntr = 0;
    double[][] points = new double[10][1];
    for (double[] point : points) {
        cntr += 100;
        point[0] = Math.random() * 100;
        point[1] = Math.random() * 100;
        data.lineTo(cntr, point[1]);
    }
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setStroke(stroke);
    g2d.draw(data);
    g2d.dispose();
}
}

我想实现类似上面的东西,因为最终我想象 GUI 变得非常复杂和简单的事件,比如绘制一个矩形来标记数据不应该在主控制器中(以防止大量的 if 测试和代码中的混乱)。

我得到的截图: 在此处输入图像描述

我想要的是: 在此处输入图像描述

编辑 虽然下面接受的答案是更好的解决方案,但如果有人可能想要使用它,我会发布这个。如果您将 prefferedSize 的大小调整为更小,它将不起作用。

public class Test {

public static void main(String[] args) {
    GeneralJFrame frame = new GeneralJFrame(1200, 800);
    frame.addPanel(new GraphPane(new Graph()), BorderLayout.CENTER);
    frame.addPanel(new ButtonPane(), BorderLayout.SOUTH);
    frame.addPanel(new ButtonPane(), BorderLayout.SOUTH);
    frame.addPanel(new ButtonPane(), BorderLayout.WEST);
    frame.addPanel(new ButtonPane(), BorderLayout.EAST);
    frame.addPanel(new ButtonPane(), BorderLayout.NORTH);
    frame.start();
}
}

public class GraphPane extends JPanel {


public GraphPane(Graph graph) {
    GridBagConstraints c = new GridBagConstraints();
    c.gridwidth = GridBagConstraints.REMAINDER;
    c.gridheight = GridBagConstraints.REMAINDER;
    c.gridx = 0;
    c.gridy = 0;
    this.setLayout(new GridBagLayout());
    this.add(graph);
    this.add(new MarkerRectangle(), c);
    this.setBackground(new Color(205, 201, 201));
}



}

public class MarkerRectangle extends JComponent implements MouseListener,
    MouseMotionListener {

private boolean draw;
private int startX, endX, startY, endY;

public MarkerRectangle() {
    this.addMouseListener(this);
    this.addMouseMotionListener(this);
    setOpaque(false);
    setPreferredSize(new Dimension(800, 600));
}

@Override
public void mouseClicked(MouseEvent e) {

}

@Override
public void mouseEntered(MouseEvent e) {

}

@Override
public void mouseExited(MouseEvent e) {

}

@Override
public void mousePressed(MouseEvent e) {
    startX = e.getX();
    startY = e.getY();
    draw = true;
}

@Override
public void mouseReleased(MouseEvent e) {
    draw = false;
    repaint();
}

@Override
public void mouseDragged(MouseEvent e) {
    endX = e.getX();
    endY = e.getY();
    repaint();
}

@Override
public void mouseMoved(MouseEvent e) {

}

@Override
// Draw rectangle
protected void paintComponent(Graphics g) {
    System.out.println(getSize());
    if (!draw)
        return;
    int w = endX-startX;
    int h = endY - startY;
    Graphics2D g2d = (Graphics2D) g.create();
    g2d.setColor(new Color(255, 165, 0));
    g2d.fillRect(startX, startY, w, h);
    g2d.dispose();
}

}

public class ButtonPane extends JPanel {
public ButtonPane() {
    add(new JButton("HELLO"));
    setBackground(Color.gray);
    setBorder(BorderFactory.createEtchedBorder(Color.white,
            Color.gray.darker()));
}
}

public class GeneralJFrame {
private JFrame frame;

public GeneralJFrame(int width, int height) {
    this.frame = new JFrame("Le Muscles");
    this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

public void addPanel(JPanel panel, String location) {
    this.frame.add(panel, location);
}

public void start() {
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
}
}

输出:(用鼠标拖动橙色) 在此处输入图像描述

4

1 回答 1

2

我仍然不完全理解你的问题。在我看来,你已经

  • 创建了几个 JPanel
  • 给定一个 JPanel 一个 MouseListener 和 MouseMotionListener
  • 您已将该 JPanel 添加到另一个 JPanel 的底部。
  • 位于底部的 JPanel 记录所有鼠标事件,因为它被告知要这样做
  • 因此,您的程序会根据您的代码按预期运行。

  • 我的问题是:它的行为怎么不像期望的那样?

  • 如果您希望添加一个附加了 MouseListeners 和 MouseMotionListeners 的 JPanel 将导致 GUI 的所有 JPanel 都被监听,当然这不会发生。要让更多的 GUI 注册鼠标事件,您必须将 MouseListeners 和 MouseMotionListeners 添加到这些组件中。在我看来,这就是我对你的问题的回答。如果我没有回答您当前面临的真正问题,请为我们澄清一下。

你说:

我想要的是上面屏幕截图中蓝色面板顶部的一个不可见(透明)面板,它与蓝色面板一样大,而不是位于底部的子面板。我希望蓝色的包含这个(应该不是问题,因为它只是一个 jcomponent)。我希望实现的是一种注册mousevents的“不可见”覆盖层,因此我不必在蓝色面板中实现这些事件。

考虑为此使用 JLayer。根据 JLayer API:

如果您只需要对复合组件进行自定义绘制或从其子组件捕获输入事件,JLayer 是一个很好的解决方案。


好的,我已经对它进行了一些实验,并想出了这样的东西:

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Path2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;

import javax.swing.*;
import javax.swing.plaf.LayerUI;

@SuppressWarnings("serial")
public class GraphPane2 extends JPanel {
   private static final int GRAPH_WIDTH = 1000;
   private static final int GRAPH_HEIGHT = 750;
   private Graph2 graph2 = new Graph2(GRAPH_WIDTH, GRAPH_HEIGHT);

   public GraphPane2() {
      LayerUI<Graph2> myLayerUI = new MyLayerUI<Graph2>();
      JLayer<Graph2> panelLayer = new JLayer<Graph2>(graph2, myLayerUI);
      setLayout(new BorderLayout());
      add(panelLayer);

      myLayerUI.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent evt) {
            if (MyLayerUI.MOUSE_RELEASED.equals(evt.getPropertyName())) {
               Rectangle rect = (Rectangle) evt.getNewValue();
               System.out.println(rect);
            }
         }
      });
   }

   private static void createAndShowGui() {
      GraphPane2 mainPanel = new GraphPane2();

      JFrame frame = new JFrame("Graph Pane2");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.setResizable(false);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

@SuppressWarnings("serial")
class MyLayerUI<V extends JComponent> extends LayerUI<V> {
   private static final Color FILL_COLOR = new Color(0, 128, 0, 128);
   public static final String MOUSE_RELEASED = "mouse released";
   private Point pressedPt;
   private Point draggedPt;
   private Rectangle rect;

   @Override
   public void paint(Graphics g, JComponent c) {
      super.paint(g, c);
      if (rect != null) {
         Graphics2D g2 = (Graphics2D) g;
         g2.setColor(FILL_COLOR);
         g2.fill(rect);
      }
   }

   public void installUI(JComponent c) {
      super.installUI(c);
      ((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
   }

   public void uninstallUI(JComponent c) {
      super.uninstallUI(c);
      ((JLayer)c).setLayerEventMask(0);
   }

   @Override
   public void eventDispatched(AWTEvent e, JLayer<? extends V> l) {
      MouseEvent mEvt = (MouseEvent) e;
      int id = mEvt.getID();
      int btn = mEvt.getButton();
      if (id == MouseEvent.MOUSE_PRESSED && btn == MouseEvent.BUTTON1) {
         pressedPt = mEvt.getPoint();
         rect = new Rectangle(pressedPt.x, pressedPt.y, 0, 0);
      }
      if (id == MouseEvent.MOUSE_PRESSED && btn != MouseEvent.BUTTON1) {
         pressedPt = null;
      }
      if (id == MouseEvent.MOUSE_DRAGGED && pressedPt != null) {
         draggedPt = mEvt.getPoint();
         int x = Math.min(draggedPt.x, pressedPt.x);
         int y = Math.min(draggedPt.y, pressedPt.y);
         int width = Math.abs(draggedPt.x - pressedPt.x);
         int height = Math.abs(draggedPt.y - pressedPt.y);
         rect = new Rectangle(x, y, width, height);
      }
      if (id == MouseEvent.MOUSE_RELEASED && pressedPt != null) {
         draggedPt = mEvt.getPoint();
         int x = Math.min(draggedPt.x, pressedPt.x);
         int y = Math.min(draggedPt.y, pressedPt.y);
         int width = Math.abs(draggedPt.x - pressedPt.x);
         int height = Math.abs(draggedPt.y - pressedPt.y);
         rect = new Rectangle(x, y, width, height);
         firePropertyChange(MOUSE_RELEASED, null, rect);
      }      
      l.repaint();
   }
}

@SuppressWarnings("serial")
class Graph2 extends JPanel {
   private static final int MAX_DATA_POINTS = 100;
   private static final int STEP = 10;
   private static final Stroke STROKE = new BasicStroke(3f);
   private Path2D path2D;
   private int width;
   private int height;
   private int[] data = new int[MAX_DATA_POINTS + 1];
   private Random random = new Random();

   public Graph2(int width, int height) {
      this.width = width;
      this.height = height;
      init();

      addComponentListener(new ComponentAdapter() {
         @Override
         public void componentResized(ComponentEvent e) {
            path2D = new Path2D.Double();
            int w = getWidth();
            int h = getHeight();
            double x = 0;
            double y = ((double) MAX_DATA_POINTS - data[0]) * h
                  / MAX_DATA_POINTS;
            path2D.moveTo(x, y);
            for (int i = 1; i < data.length; i++) {
               x = (i * w) / (double) MAX_DATA_POINTS;
               y = ((double) MAX_DATA_POINTS - data[i]) * h
                     / (double) MAX_DATA_POINTS;
               path2D.lineTo(x, y);
            }
         }
      });
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(width, height);
   }

   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (path2D != null) {
         Graphics2D g2d = (Graphics2D) g;
         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
               RenderingHints.VALUE_ANTIALIAS_ON);
         g2d.setStroke(STROKE);
         g2d.draw(path2D);
      }
   };

   private void init() {
      // create and fill random data
      data[0] = 0;
      boolean up = true;
      // max and min data values -- used for normalization
      int min = Integer.MAX_VALUE;
      int max = Integer.MIN_VALUE;
      for (int i = 1; i < data.length; i++) {
         up = random.nextInt(4) < 3 ? up : !up;
         if (up) {
            data[i] = data[i - 1] + random.nextInt(STEP);
         } else {
            data[i] = data[i - 1] - random.nextInt(STEP);
         }

         if (data[i] > max) {
            max = data[i];
         }
         if (data[i] < min) {
            min = data[i];
         }
      }

      // normalize the data
      for (int i = 0; i < data.length; i++) {
         int datum = (MAX_DATA_POINTS * (data[i] - min)) / (max - min);
         data[i] = datum;
      }

   }
}

这看起来像:

在此处输入图像描述

于 2013-04-28T00:47:39.930 回答