我想创建一个自定义按钮,其中包含一个大小方块,分别具有以下颜色:#2980b9
#3498db
。
小方块将位于大方块内部,当光标位于上方或单击时,它将增加其大小,同时颜色将更改为更清晰的(#4AA3DF
)。 / p>
问题是只打印出较小的一个,甚至打印得不好;因为它出现在窗口的左上角。
此外,MouseListener
函数根本没有被使用。
这是Button
类:
public class Button extends JComponent implements MouseListener {
private static final long serialVersionUID = 1L;
JFrame frame = new JFrame();
public Button(JFrame frame) {
enableInputMethods(true);
addMouseListener(this);
this.frame = frame;
}
// Mouse activity //DELETED
MouseEvent mouseEvent; //DELETED
// Window's width and height.
int width = (int) frame.getWidth();
int height = (int) frame.getHeight();
// Squares's sizes.
int bigSquareXSize = 200;
int bigSquareYSize = 200;
int smallSquareXSize = 180;
int smallSquareYSize = 180;
// smallSquare color.
volatile String color = "#3498db";
//I think that I should do something with the update method,
//but i'm not sure about what (sorry, I know this is stupid).
public void update() {
}
@Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
// Squares's X and Y positions.
int bigSquareXPosition = width / 2 - bigSquareXSize / 2;
int bigSquareYPosition = height / 2 - bigSquareYSize / 2;
int smallSquareXPosition = width / 2 - smallSquareXSize / 2;
int smallSquareYPosition = height / 2 - smallSquareYSize / 2;
g.setColor(Color.decode("#2980b9"));
g2.setColor(Color.decode(color));
g.fillRect(bigSquareXPosition, bigSquareYPosition, bigSquareXSize, bigSquareYSize);
g2.fillRect(smallSquareXPosition, smallSquareYPosition, smallSquareXSize, smallSquareYSize);
}
// Returns a true value if the cursor is placed over the smallSquare.
public boolean insideArea(MouseEvent e) {
boolean value = false;
int smallSquareXPosition = width / 2 - smallSquareXSize / 2;
int smallSquareYPosition = height / 2 - smallSquareYSize / 2;
if (e.getX() > smallSquareXPosition && e.getX() < smallSquareXPosition + smallSquareXSize) {
if (e.getY() > smallSquareYPosition && e.getY() < smallSquareYPosition + smallSquareYSize) {
value = true;
}
}
return value;
}
volatile boolean clicked = false;
@Override
public void mouseClicked(MouseEvent e) {
if (insideArea(e)) {
clicked = !clicked;
if (clicked) {
color = "#4AA3DF";
smallSquareXSize = 190;
smallSquareYSize = 190;
}
} else {
color = "#3498db";
smallSquareXSize = 180;
smallSquareYSize = 180;
}
this.repaint();
}
@Override
public void mouseEntered(MouseEvent e) {
if (!clicked) {
if (insideArea(e)) {
color = "#4AA3DF";
smallSquareXSize = 190;
smallSquareYSize = 190;
}
}
this.repaint();
}
@Override
public void mouseExited(MouseEvent e) {
if (!clicked) {
if (insideArea(e)) {
color = "#3498db";
smallSquareXSize = 180;
smallSquareYSize = 180;
}
}
this.repaint();
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
这是StartingPoint
类:
public class StartingPoint implements Runnable {
Thread thread = new Thread(this);
static JFrame frame = new JFrame("BUTTON!");
static Button button = new Button(frame);
public static void main(String[] args) {
//Frame creation
JFrame frame = new JFrame("BUTTON!");
frame.setSize(600, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());
panel.add(button);
frame.add(panel);
}
@Override
public void run() {
while (true) {
button.update();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
这就是控制台所说的:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at Button.insideArea(Button.java:62)
at Button.mouseEntered(Button.java:92)
据我所知,问题与frame
有关,但我不知道如何解决。
我查找了java.lang.NullPointerException
异常,但我理解的是,当您不调用setSize()
方法或类似方法时会发生这种情况,但我做了。
问题是什么,解决问题的最佳方法是什么?
已编辑:我发现导致控制台说出mouseEvent
的内容的原因。现在,每次我想引用光标时,我都会在方法定义的括号之间使用e
。
控制台没有错误;但是,事情以相同的方式打印,并且MouseListener仍然无法正常工作。
编辑:我没有例外,因为有MouseEvent mouseEvent
,但其他问题仍然存在。
答案 0 :(得分:2)
有几个问题。
这些行应该在paint方法中:
int smallSquareXPosition = width / 2 - smallSquareXSize / 2;
int smallSquareYPosition = height / 2 - smallSquareYSize / 2;
就目前而言,这些值在对象初始化时分配一次。但是您的绘制方法希望它们是动态的,因为您的事件侦听器会更改smallSquareXSize
和smallSquareXSize
。
由于只能从paint方法访问这些变量,所以在paint方法中移动所有sch变量声明:
public void paintComponent(Graphics g) {
int bigSquareXPosition = width / 2 - bigSquareXSize / 2;
int bigSquareYPosition = height / 2 - bigSquareYSize / 2;
int smallSquareXPosition = width / 2 - smallSquareXSize / 2;
int smallSquareYPosition = height / 2 - smallSquareYSize / 2;
Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.decode("#2980b9"));
g2.setColor(Color.decode(color));
g.fillRect(bigSquareXPosition, bigSquareYPosition, bigSquareXSize, bigSquareYSize);
g2.fillRect(smallSquareXPosition, smallSquareYPosition, smallSquareXSize, smallSquareYSize);
}
这也遵循一种良好的通用编码实践,即声明变量使得它们具有尽可能小的范围。在这种情况下,您将把所有方法的范围缩小到仅使用它们的方法。虽然它在这种情况下不适用,但如果可能的话,你应该进一步将范围缩小到一个代码块(例如在循环或if
块等内)。
另一个问题更微妙。它是并发之一,即在多线程环境中运行。发送鼠标事件的线程是与主线程不同的线程。此外,它可能是每个鼠标事件的不同线程(我不确定)。并发线程的一个含义是,在一个线程中对实例变量所做的更改可能不会被视为&#34;通过其他线程。具体来说,这个字段:
boolean clicked = false;
和这一行:
clicked = !clicked;
这个&#34;问题&#34;是由于java内存模型,它简单地说每个线程可以缓存字段值的副本,这意味着如果一个线程为其分配一个新值,另一个线程可能会或可能不会看到此更改。 / p>
但不要惊慌 - 有一个简单的解决办法:
volatile boolean clicked = false;
volatile
关键字告诉java永远不会缓存该值 - 始终检查由创建该对象的线程管理的内存中的值。
我不确定您使用的框架是否可能针对各种类型的鼠标事件使用不同的线程,但如果确实如此,则需要volatile
。如果不是,那么如果不需要,volatile
就不会有任何伤害。
答案 1 :(得分:1)
问题是您的➜ dcos-vagrant git:(master) dcos marathon app add https://dcos.io/docs/1.7/usage/nginx.json
zsh: command not found: dcos
变量从未初始化。您可以初始化它,然后每次将其传递给MouseEvent mouseEvent;
,或者您可以完全删除它,并直接使用该事件,如下图所示。
insideArea
在这里
@Override
public void mouseClicked(MouseEvent e) {
if (insideArea(e)) {
...
在这里
@Override
public void mouseEntered(MouseEvent e) {
if (!clicked) {
if (insideArea(e)) {
...