2016-05-05 3 views
4

Я хочу создать пользовательскую кнопку, состоящую из большого и небольшого квадрата, которые имеют следующие цвета: #2980b9#3498db. Маленький квадрат будет внутри большого, и он увеличит его размер, когда курсор будет помещен выше или если он будет нажат, и в то же время цвет изменится на более чистый (#4AA3DF).Проблемы с печатью пользовательской кнопки и создание работы MouseListener

Проблема в том, что распечатывается только меньшая, и она даже не распечатана хорошо; как он отображается в верхнем левом углу окна. Кроме того, функции 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() или похожий, но я это сделал.

В чем проблема и что это лучший способ ее решить?

EDITED: Я понял, что причиной того, что консоль сказала, что она сказала, была mouseEvent. Теперь каждый раз, когда я хочу ссылаться на курсор, я буду придерживаться e между круглой скобкой при определении метода. В консоли нет ошибок; однако вещи печатаются таким же образом, и MouseListener все еще не работает.

EDIT: У меня больше нет исключений, потому что это произошло из-за MouseEvent mouseEvent, но другие проблемы все еще существуют.

+0

Что такое код в строке 62 класса Button? – Bohemian

+0

У меня нет никаких исключений, потому что они были вызваны «MouseEvent mouseEvent», но вещи распечатываются одинаково, а MouseListener не работает. – SpaceCore186

ответ

1

Проблема в том, что переменная MouseEvent mouseEvent; никогда не инициализируется. Вы можете инициализировать его, а затем передавать его до insideArea каждый раз, или вы можете полностью удалить его и просто использовать событие непосредственно, как показано ниже.

@Override 
public void mouseClicked(MouseEvent e) { 
    if (insideArea(e)) { 
    ... 

А вот

@Override 
public void mouseEntered(MouseEvent e) { 
    if (!clicked) { 
     if (insideArea(e)) { 
      ... 

и здесь

@Override 
public void mouseExited(MouseEvent e) { 
    if (!clicked) { 
     if (insideArea(e)) { 
     ... 
+0

Я просто понял, что (вероятно, пока вы печатали свой ответ) хахаха! Знаете ли вы, почему вещи распечатываются так, как они печатаются? Это похоже на то, что я не устанавливаю размер моего фрейма, но я это делаю. – SpaceCore186

+0

Вы имеете в виду тот факт, что ваша кнопка не перекрашивается? – Kalenda

2

Есть несколько проблем.

Эти линии должны быть внутри метода краски:

int smallSquareXPosition = width/2 - smallSquareXSize/2; 
int smallSquareYPosition = height/2 - smallSquareYSize/2; 

Как можно заметить, эти значения присваиваются один раз при инициализации объекта.Но ваш метод рисования предполагает, что они будут динамичными, потому что ваш слушатель событий изменяется smallSquareXSize и smallSquareXSize.

Поскольку эти переменные только доступны из метода краски, переместить все 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 блока и т. Д.), Если это возможно.


Другая проблема более тонкая. Это одно из параллелизма, то есть работа в многопоточной среде. Поток, который отправляет событие мыши, представляет собой другой поток для основного потока. Кроме того, это может быть другой поток каждого события мыши (я не уверен). Одним из последствий параллельных потоков является то, что изменения в переменных экземпляра, сделанные в одном потоке, могут не отображаться другими потоками. В частности, в этом поле:

boolean clicked = false; 

и эта линия:

clicked = !clicked; 

Эта «проблема» связана с моделью памяти Java, которая просто положить говорит, что каждый поток может кэшировать копию значения поля , что означает, что если один поток назначает ему новое значение, другой поток может увидеть или не увидеть это изменение.

Но не паникуйте - есть легко исправить:

volatile boolean clicked = false; 

volatile ключевое слово говорит Java никогда не кэшировать значения - всегда проверять значение в памяти, управляемой потоком, который создал объект.

Я не уверен, что используемая вами фреймворк может использовать разные потоки для различных типов событий мыши, но если это так, вам нужно volatile. Если нет, никакого вреда от этого не будет volatile, если это не обязательно.

+0

Теперь даже не распечатывается рамка. Я только что редактировал свой вопрос, чтобы вы могли увидеть новый исходный код. – SpaceCore186

+0

Должен ли я что-то делать с помощью метода 'update'? – SpaceCore186

+0

Сделайте «цвет» неустойчивым, по тем же причинам. – Bohemian