我正在编写一个简单的图形编辑器,到目前为止我可以绘制一些数字,移动和放大它们。我试图让用户改变图形的颜色。在我右键单击一个形状后,会出现一个带有颜色的弹出菜单供选择。但无论我做什么 - 形状的颜色都不会改变。 :/我希望得到帮助,我花了很多时间在上面,但不知道如何解决它。 :/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
public class PaintPanel extends JPanel implements MouseListener, MouseMotionListener, MouseWheelListener
{
public RadioMenu radio = new RadioMenu();
private ArrayList <Point2D.Double> points = new ArrayList<>();
private ArrayList <Shape> figures = new ArrayList<>();
private Color mainColor = Color.blue;
private Color bgColor = Color.white;
private Color special = Color.red;
private double scrollSpeed = 5;
private int pointsize = 4;
private int near = 15;
private int index;
private ColorMenu colorMenu = new ColorMenu();
public PaintPanel()
{
super();
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
setLayout(new BorderLayout());
setBackground(bgColor);
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
drawGraphics(g2d);
}
private void drawGraphics(Graphics2D g2d)
{
int i = 0;
for (Shape s : figures)
{
g2d.setColor(mainColor);
if (s instanceof MyEllipse2D)
{
g2d.setColor(((MyEllipse2D) s).color);
System.out.println(g2d.getColor());
}
else if (s instanceof MyRectangle2D)
{
g2d.setColor(((MyRectangle2D) s).color);
System.out.println(g2d.getColor());
}
else if (s instanceof MyPolygon2D)
{
g2d.setColor(((MyPolygon2D) s).color);
System.out.println(g2d.getColor());
}
if (g2d.getColor() != bgColor)
{
g2d.setColor(mainColor);
} else
{
g2d.setColor(mainColor);
g2d.draw(s);
}
++i;
}
i = 0;
for (Point2D.Double p : points)
{
if (i == 0)
{
g2d.setColor(special);
g2d.fillOval((int) p.getX(), (int) p.getY(), pointsize, pointsize);
g2d.setColor(mainColor);
} else
{
g2d.fillOval((int) p.getX(), (int) p.getY(), pointsize, pointsize);
}
++i;
}
}
@Override
public void mousePressed(MouseEvent e)
{
if(e.getButton()==MouseEvent.BUTTON1)
switch (radio.getChoice())
{
case "Okrag":
points.clear();
points.add(new Point2D.Double(e.getX(),e.getY()));
repaint();
break;
case "Prostokat":
points.clear();
points.add(new Point2D.Double(e.getX(),e.getY()));
repaint();
break;
case "Edycja":
index = isSelected(e);
break;
}
else if(e.getButton()==MouseEvent.BUTTON3 && radio.getChoice().equals("Edycja"))
{
index = isSelected(e);
if(index >= 0)
{
colorMenu.doPop(e);
}
}
}
@Override
public void mouseReleased(MouseEvent e)
{
if(e.getButton()==MouseEvent.BUTTON1)
switch (radio.getChoice())
{
case "Okrag":
points.add(new Point2D.Double(e.getX(),e.getY()));
figures.add(new MyEllipse2D(points.get(0),points.get(1),bgColor));
points.clear();
break;
case "Prostokat":
points.add(new Point2D.Double(e.getX(),e.getY()));
figures.add(new MyRectangle2D(points.get(0),points.get(1),bgColor));
points.clear();
break;
case "Wielokat":
if(points.size() != 0 && points.get(0).distance(e.getX(),e.getY())<=near)
{
figures.add(new MyPolygon2D(points,bgColor));
points.clear();
}
else
{
points.add(new Point2D.Double(e.getX(),e.getY()));
}
break;
case "Edycja":
points.clear();
}
repaint();
}
@Override
public void mouseDragged(MouseEvent e)
{
if(index>=0 && radio.getChoice().equals("Edycja"))
{
if (figures.get(index) instanceof MyEllipse2D)
{
((MyEllipse2D) figures.get(index)).move(new Point2D.Double(e.getX(),e.getY()));
}
else if (figures.get(index) instanceof MyRectangle2D)
{
((MyRectangle2D) figures.get(index)).move(new Point2D.Double(e.getX(),e.getY()));
}
else if(figures.get(index) instanceof MyPolygon2D)
{
((MyPolygon2D) figures.get(index)).move(new Point2D.Double(e.getX(),e.getY()));
}
repaint();
}
}
@Override
public void mouseWheelMoved(MouseWheelEvent e)
{
index = isSelected(e);
if(radio.getChoice().equals("Edycja"))
{
if (index>=0)
{
if (figures.get(index) instanceof MyEllipse2D)
{
((MyEllipse2D) figures.get(index)).scale(e.getPreciseWheelRotation(), scrollSpeed);
}
else if (figures.get(index) instanceof MyRectangle2D)
{
((MyRectangle2D) figures.get(index)).scale(e.getPreciseWheelRotation(), scrollSpeed);
}
else if(figures.get(index) instanceof MyPolygon2D)
{
((MyPolygon2D) figures.get(index)).scale(e.getPreciseWheelRotation(), scrollSpeed);
}
repaint();
}
}
}
private int isSelected(MouseEvent e)
{
int i;
for(i=figures.size()-1;i>=0;--i)
{
if(figures.get(i).contains(e.getPoint()))
{
return i;
}
}
return -1;
}
@Override
public void mouseClicked(MouseEvent e)
{
index = isSelected(e);
if(index >= 0 )
{
colorMenu.doPop(e);
if(e.getButton()==MouseEvent.BUTTON3 && radio.getChoice().equals("Edycja"))
{
colorMenu.doPop(e);
if(figures.get(index) instanceof MyEllipse2D)
((MyEllipse2D) figures.get(index)).color = colorMenu.color;
else if(figures.get(index) instanceof MyRectangle2D)
((MyRectangle2D) figures.get(index)).color = colorMenu.color;
else if(figures.get(index) instanceof MyPolygon2D)
((MyPolygon2D) figures.get(index)).color = colorMenu.color;
System.out.println(colorMenu.color);
//colorMenu.color = bgColor;
}
repaint();
}
}
@Override
public void mouseEntered(MouseEvent e)
{
}
@Override
public void mouseExited(MouseEvent e)
{
}
@Override
public void mouseMoved(MouseEvent e)
{
}
}
ColorMenu
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
public class ColorMenu extends JPopupMenu implements ActionListener
{
private ArrayList<JMenuItem> items = new ArrayList<JMenuItem>();
private ArrayList<Color> colors = new ArrayList<Color>();
public Color color;
public ColorMenu()
{
super();
colors.add(Color.black);
colors.add(Color.blue);
colors.add(Color.cyan);
colors.add(Color.gray);
colors.add(Color.green);
colors.add(Color.magenta);
colors.add(Color.orange);
colors.add(Color.red);
colors.add(Color.yellow);
colors.add(Color.white);
for (Color c : colors)
{
items.add(new JMenuItem(c.toString()));
}
for(JMenuItem i: items)
{
i.addActionListener(this);
add(i);
}
}
public void doPop(MouseEvent e)
{
show(e.getComponent(), e.getX(), e.getY());
}
@Override
public void actionPerformed(ActionEvent e)
{
Object source = e.getSource();
int j=0;
for(JMenuItem i: items)
{
if(i == source)
{
break;
}
++j;
}
this.color = colors.get(j);
}
}
MyRectangle2D
import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
public class MyRectangle2D extends Rectangle2D.Double implements Shape
{
private Point2D.Double center;
private double width,hight;
public Color color;
public MyRectangle2D()
{}
public MyRectangle2D(Point2D.Double p1, Point2D.Double p2, Color color)
{
super();
this.color = color;
double x1 = p1.getX();
double y1 = p1.getY();
double x2 = p2.getX();
double y2 = p2.getY();
if(x1<=x2 && y1>=y2)
{
width=x2-x1;
hight=y1-y2;
setRect(x1,y2,width,hight);
}
else if(x1<=x2 && y1<=y2)
{
width=x2-x1;
hight=y2-y1;
setRect(x1,y1,width,hight);
}
else if (x1>=x2 && y1<=y2)
{
width=x1-x2;
hight=y2-y1;
setRect(x2,y1,width,hight);
}
else if(x1>=x2 && y1>=y2)
{
width=x1-x2;
hight=y1-y2;
setRect(x2,y2,width,hight);
}
center = new Point2D.Double(x1 + (x2-x1)/2,y1+(y2-y1)/2);
}
public void scale(double amount, double scale)
{
double change = -1*amount*scale;
width += change;
hight += change;
setRect(center.getX()-width/2,center.getY()-hight/2,width,hight);
}
public void move (Point2D.Double p)
{
center = p;
setRect();
}
private void setRect()
{
setRect(center.getX()-width/2,center.getY()-hight/2,width,hight);
}
}
MyPolygon2D
import java.awt.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
public class MyPolygon2D extends Polygon implements Shape
{
private ArrayList<MyVector> vectors = new ArrayList<>();
private Point2D.Double center;
private int size;
public Color color;
public MyPolygon2D()
{}
public MyPolygon2D(ArrayList<Point2D.Double> points, Color color)
{
super();
this.color = color;
size = points.size();
for(int i=0; i<size;++i)
{
addPoint((int)points.get(i).getX(),(int)points.get(i).getY());
}
center();
setVectors();
}
public void scale(double amount, double scale)
{
double change = -1*amount*scale;
for (int i=0;i<size;++i)
{
vectors.get(i).x *= (100.0+change)/100.0;
vectors.get(i).y *= (100.0+change)/100.0;
Point2D.Double curr = new Point2D.Double(center.getX()+vectors.get(i).x,center.getY()+vectors.get(i).y);
xpoints[i] = (int)curr.getX();
ypoints[i] = (int)curr.getY();
}
invalidate();
}
public void move (Point2D.Double p)
{
MyVector change = new MyVector(center,p);
center = p;
for(int i=0;i<size;++i)
{
xpoints[i] += (int)change.x;
ypoints[i] += (int)change.y;
}
invalidate();
}
public void setColor(Color color)
{
this.color = color;
}
public Color getColor()
{
return this.color;
}
@Override
public boolean contains(Point p)
{
int maxx=0, maxy=0, minx=Integer.MAX_VALUE, miny=Integer.MAX_VALUE;
for (int i=0;i<size;++i)
{
if(xpoints[i]>=maxx)
maxx = xpoints[i];
if(xpoints[i]<=minx)
minx = xpoints[i];
if(ypoints[i]>=maxy)
maxy = ypoints[i];
if(ypoints[i]<=miny)
miny = ypoints[i];
}
if(p.getX() <= maxx && p.getX() >= minx && p.getY() <= maxy && p.getY() >=miny)
return true;
else
return false;
}
private void setVectors()
{
for(int i=0; i<size;++i)
{
vectors.add(new MyVector(center,new Point2D.Double(xpoints[i],ypoints[i])));
}
}
private void center()
{
center = new Point2D.Double(getBounds2D().getX()+getBounds2D().getWidth()/2,getBounds2D().getY()+getBounds2D().getHeight()/2);
}
private class MyVector
{
public double x, y;
public MyVector(Point2D.Double p1, Point2D.Double p2)
{
x=p2.getX()-p1.getX();
y=p2.getY() - p1.getY();
}
}
}
MyEllipse2D
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
public class MyEllipse2D extends Ellipse2D.Double implements Shape
{
private double radius;
private Point2D.Double center;
public Color color;
public MyEllipse2D(Point2D.Double p1, Point2D.Double p2, Color color)
{
super();
this.color = color;
center = p1;
radius = (p1.distance(p2));
setFrame();
}
public void scale(double amount, double scale)
{
double change = -1*amount*scale;
radius += change;
setFrame();
}
public void move (Point2D.Double p)
{
center = p;
setFrame();
}
public void setColor(Color color)
{
this.color = color;
System.out.println(this.color);
}
public Color getColor()
{
return this.color;
}
private void setFrame()
{
setFrame(center.getX()-radius,center.getY()-radius,2*radius,2*radius);
}
}
RadioMenu
import javax.swing.*;
import java.awt.*;
public class RadioMenu extends JPanel
{
private int amount = 4;
private JRadioButton[] options = new JRadioButton[amount];
private ButtonGroup group = new ButtonGroup();
private String[] names = {"Okrag","Prostokat","Wielokat","Edycja"};
private Font font = new Font("Times New Roman",Font.BOLD,16);
public RadioMenu()
{
super();
setLayout(new GridLayout(1,amount));
for(int i=0;i<amount;++i)
{
if(i!=0)
options[i] = new JRadioButton(names[i],false);
else
options[i] = new JRadioButton(names[i],true);
group.add(options[i]);
add(options[i]);
options[i].setFont(font);
}
}
public String getChoice()
{
for(int i=0; i<amount; ++i)
{
if(options[i].isSelected())
return options[i].getText();
}
return "";
}
}
框
import javax.swing.*;
import java.awt.*;
public class Frame extends JFrame
{
private Dimension prefsize = new Dimension(800,600);
private Dimension minSize = new Dimension(400,200);
private Menu menu = new Menu();
private PaintPanel panel = new PaintPanel();
public Frame()
{
super();
setVisible(true);
setPreferredSize(prefsize);
setMinimumSize(minSize);
setLayout(new BorderLayout());
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(panel,BorderLayout.CENTER);
JPanel upper = new JPanel();
upper.setLayout(new GridLayout(2,1));
upper.add(menu);
upper.add(panel.radio);
add(upper,BorderLayout.NORTH);
pack();
}
}
MyAplet
import javax.swing.*;
public class MyAplet extends JApplet
{
public void init()
{
Frame main = new Frame();
}
}
菜单
import javax.swing.*;
public class Menu extends JMenuBar
{
private JMenu info;
//private JMenuItem x;
public Menu()
{
super();
info = new JMenu("info");
//info.add(x);
add(info);
}
}
答案 0 :(得分:0)
首先,您应该显示JPopupMenu以仅响应一种类型的事件。您当前正在mousePressed方法中调用colorMenu.doPop(e)
,在mouseClicked方法中调用两次。
为了确保只在你应该的时候显示JPopupMenu,你应该使用JPopupMenu.isPopupTrigger方法。这样,菜单将显示在mousePress 或 mouseClick上,但不会同时显示。您不希望多次显示JPopupMenu以响应单个鼠标操作!
private int selectedIndex = -1;
// ...
@Override
public void mousePressed(MouseEvent e)
{
selectedIndex = isSelected(e);
if (colorMenu.isPopupTrigger(e)) {
colorMenu.doPop(e);
} else if (e.getButton() == MouseEvent.BUTTON1) {
// ...
@Override
public void mouseClicked(MouseEvent e)
{
if (selectedIndex >= 0 && colorMenu.isPopupTrigger(e) && radio.getChoice().equals("Edycja"))
{
colorMenu.doPop(e);
下一个问题是您在用户进行颜色选择之前尝试阅读colorMenu.color
。当您致电colorMenu.doPopup(e)
时,会显示菜单并立即返回该方法。它不等待用户进行选择并关闭菜单。您的代码正在尝试立即使用colorMenu.color
,但尚未设置。
了解用户何时选择颜色的唯一方法是使用ActionListener。您的ColorMenu类有一个ActionListener,但是目前PaintPanel类无法知道ActionListener何时被触发。
您可以使用继承的listenerList对象为ColorMenu类提供通知其他类中的侦听器的功能:
public void addChangeListener(ChangeListener listener) {
listenerList.add(ChangeListener.class, listener);
}
public void removeChangeListener(ChangeListener listener) {
listenerList.remove(ChangeListener.class, listener);
}
public ChangeListener[] getChangeListeners() {
return listenerList.getListeners(ChangeListener.class);
}
protected void fireChangeListeners() {
ChangeEvent event = new ChangeEvent(this);
for (ChangeListener listener : getChangeListeners()) {
listener.stateChanged(event);
}
}
(ChangeListener和ChangeEvent位于javax.swing.event包中。)
这允许其他类监听用户选择。您想要更改ColorMenu以实际通知这些侦听器:
@Override
public void actionPerformed(ActionEvent e)
{
// (other code) ...
this.color = colors.get(j);
fireChangeListeners();
}
现在,您可以让PaintPanel监听用户选择:
public PaintPanel()
{
super();
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
setLayout(new BorderLayout());
setBackground(bgColor);
colorMenu.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent event) {
if (selectedIndex >= 0)
{
Shape figure = figures.get(selectedIndex);
if (figure instanceof MyEllipse2D)
((MyEllipse2D) figure).color = colorMenu.color;
else if (figure instanceof MyRectangle2D)
((MyRectangle2D) figure).color = colorMenu.color;
else if (figure instanceof MyPolygon2D)
((MyPolygon2D) figure).color = colorMenu.color;
}
}
});
}
您应该从mouseClicked
删除该换色代码,因为在那里调用它永远无效,因为用户尚未选择颜色:
@Override
public void mouseClicked(MouseEvent e)
{
if (selectedIndex >= 0 && colorMenu.isPopupTrigger(e) && radio.getChoice().equals("Edycja"))
{
colorMenu.doPop(e);
// Nothing else to do here, since the user has not selected a color yet.
}
}
答案 1 :(得分:0)
基本问题是,您需要知道何时从菜单中选择颜色。可以通过多种不同方式解决问题。
您可以继续使用MouseListener
来监控mousePressed
,mouseReleased
和mouseClicked
事件,但您应该使用MouseEvent#isPopupTrigger
属性确定何时应显示弹出窗口,因为不同平台的触发器不同。
另一种解决方案是使用JComponent#setComponentPopupMenu
所有组件来处理动作本身。然而,这需要稍微改变设计。
这种方法将责任重新放回到组件上,而不是分成不同的类,就像你现在拥有ColorMenu
一样。
首先,我们需要知道所选的形状,因此,对于PaintPanel
,我们需要添加一个新属性
private Shape selectedShape;
所有这一切都决定了用户选择了哪种形状。
接下来我们需要更新形状颜色。这可以通过简单地向paintPanel
方法添加新方法来实现,该方法在要更改颜色时调用。
public void setShapeColor(Color color) {
//...
}
接下来,我们利用Action
API创建一个简单的ColorAction
。
这是一个独立的工作单元,其中包含需要在菜单(或按钮)上显示的信息,并确定触发时要采取的操作。
public class ColorAction extends AbstractAction {
private Color color;
public ColorAction(String name, Color color) {
super(name);
this.color = color;
}
public Color getColor() {
return color;
}
@Override
public boolean isEnabled() {
return getSelectedShape() != null;
}
@Override
public void actionPerformed(ActionEvent e) {
setShapeColor(color);
}
}
在这种情况下,PaintPanel
这个简单的内部类会在触发时调用setShapeColor
。
“但是等等”,我听到你说:“只有选择了一个形状才能选择颜色!”啊,我们可以通过Action
方法控制isEnabled
,只有在实际选择了形状时才会返回true。
这可能看起来有点违反直觉,但是当没有显示任何内容时不显示弹出菜单,它会让用户认为永远不会显示任何弹出菜单,至少这样,我们可以显示带有选项的弹出菜单禁用。
下一个技巧是尝试确定所选内容。不幸的是,在我的测试过程中,操作系统消耗了mousePressed
事件,这对于做出这个决定是最有用的,但是并不是全部都会丢失。
JComponent
会在弹出窗口显示之前调用getPopupLocation
,传入触发它的MouseEvent
,我们可以利用此功能来确定选择哪种形状< / p>
@Override
public Point getPopupLocation(MouseEvent event) {
selectedShape = null;
for (int i = figures.size() - 1; i >= 0; --i) {
if (figures.get(i).contains(event.getPoint())) {
selectedShape = figures.get(i);
}
}
return super.getPopupLocation(event);
}
更完整的例子......
public class PaintPanel extends JPanel implements MouseListener, MouseMotionListener, MouseWheelListener {
private ArrayList<Point2D.Double> points = new ArrayList<>();
private ArrayList<Shape> figures = new ArrayList<>();
private Shape selectedShape;
private Color mainColor = Color.blue;
private Color bgColor = Color.white;
private Color special = Color.red;
private double scrollSpeed = 5;
private int pointsize = 4;
private int near = 15;
private int index;
public PaintPanel() {
super();
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
setLayout(new BorderLayout());
setBackground(bgColor);
setComponentPopupMenu(makePopupMenu());
}
protected JPopupMenu makePopupMenu() {
JPopupMenu menu = new JPopupMenu();
menu.add(new ColorAction("Black", Color.black));
menu.add(new ColorAction("Blue", Color.blue));
menu.add(new ColorAction("Cyan", Color.cyan));
menu.add(new ColorAction("Grey", Color.gray));
menu.add(new ColorAction("Green", Color.green));
menu.add(new ColorAction("Megenta", Color.magenta));
menu.add(new ColorAction("Orange", Color.orange));
menu.add(new ColorAction("Red", Color.red));
menu.add(new ColorAction("Yellow", Color.yellow));
menu.add(new ColorAction("White", Color.white));
return menu;
}
public Shape getSelectedShape() {
return selectedShape;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
public Point getPopupLocation(MouseEvent event) {
selectedShape = null;
for (int i = figures.size() - 1; i >= 0; --i) {
if (figures.get(i).contains(event.getPoint())) {
selectedShape = figures.get(i);
}
}
return super.getPopupLocation(event);
}
@Override
public void mouseClicked(MouseEvent e) {
//...
}
@Override
public void mousePressed(MouseEvent e) {
//...
}
@Override
public void mouseReleased(MouseEvent e) {
//...
}
@Override
public void mouseEntered(MouseEvent e) {
//...
}
@Override
public void mouseExited(MouseEvent e) {
//...
}
@Override
public void mouseDragged(MouseEvent e) {
//...
}
@Override
public void mouseMoved(MouseEvent e) {
//...
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
//...
}
public void setShapeColor(Color color) {
//...
}
public class ColorAction extends AbstractAction {
private Color color;
public ColorAction(String name, Color color) {
super(name);
this.color = color;
}
public Color getColor() {
return color;
}
@Override
public boolean isEnabled() {
return getSelectedShape() != null;
}
@Override
public void actionPerformed(ActionEvent e) {
setShapeColor(color);
}
}
}
您可以通过其他几种方式实现这一目标,但我将使用这些基本原则是设计的基石。
例如,你可以有一个“颜色模型”,它被传递给弹出菜单类,当选择颜色时会更新