Перемещение графики в JPanel - PullRequest
0 голосов
/ 26 февраля 2019

Я бы хотел сделать овальное перемещение из одного места в другое в JPanel при нажатии кнопки.Это код, который я придумал.Однако, когда я нажимаю кнопку, все происходит сразу, без видимого движения, замедление от начала до конца.Овал просто появляется в новом месте.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.JPanel;
public class testtest implements ActionListener{
    JButton button;
    MyDrawPanel panel;
    int x = 0;
    int y = 0;
    public static void main(String[]args){
        testtest test = new testtest();
        test.go();
    }
    public void go(){
        JFrame frame = new JFrame("Balloon Balls");
        panel = new MyDrawPanel();
        button = new JButton("Restart");
        button.addActionListener(this);
        panel.add(button);
        frame.setSize(300, 300);
        frame.add(panel);
        frame.setVisible(true);
    }

    @Override
    public void actionPerformed (ActionEvent e){
        for(int i=0;i<130;i++){
            x++;
            y++;
            panel.repaint();
            try {
                Thread.sleep(100);
            } catch(Exception ex) { }
        }
    }
    class MyDrawPanel extends JPanel{
        @Override
        public void paintComponent(Graphics g){
            g.fillOval(x, y, 30, 30);
            g.setColor(Color.BLACK);

        }
    }
}

Ответы [ 3 ]

0 голосов
/ 26 февраля 2019

Swing является однопоточным и не безопасным для потоков.

Использование Thread.sleep(100) в ActionListener блокирует поток диспетчеризации событий, предотвращая окрашивание чего-либо.Новый проход рисования не произойдет, пока не будет создан метод actionPerformed.

Подробнее см. Параллельность в Swing .

Swing также не безопасен для потоков,это означает, что вы никогда не должны вносить изменения в пользовательский интерфейс вне контекста EDT.

Самое простое решение - использовать Swing Timer, который позволит регулярно устанавливатьсинхронизированные обратные вызовы, которые выполняются в потоке диспетчеризации событий, но не блокируют EDT.

Вам также не хватает одной из важных концепций ОО, инкапсуляции.Свойства x / y должны фактически управляться MyDrawPanel, а не testtest

Например ...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class testtest implements ActionListener {

    JButton button;
    MyDrawPanel panel;

    public static void main(String[] args) {
        testtest test = new testtest();
        test.go();

    }

    public void go() {
        JFrame frame = new JFrame("Balloon Balls");
        panel = new MyDrawPanel();
        button = new JButton("Restart");
        button.addActionListener(this);
        panel.add(button);
        frame.add(panel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

    }

    private Timer timer;

    public void actionPerformed(ActionEvent e) {
        if (timer != null) {
            return;
        }
        timer = new Timer(100, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                if (panel.update()) {
                    timer.stop();
                    timer = null;
                }
            }
        });
        timer.start();
    }

    class MyDrawPanel extends JPanel {

        private int xPosy = 0;
        private int yPosy = 0;

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

        public boolean update() {
            xPosy++;
            yPosy++;
            repaint();

            return xPosy > getWidth() || yPosy > getHeight();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);            
            g.fillOval(xPosy, yPosy, 30, 30);
            g.setColor(Color.BLACK);
        }
    }
}
0 голосов
/ 26 февраля 2019

В дополнение к объяснению проблем с потоками Swing в ответе MadProgrammer я бы порекомендовал отделить графический интерфейс от его управления с помощью реализации MVC Pattern .Это обеспечивает лучшую инкапсуляцию, лучшее разделение обязанностей и упрощает использование потоков для обработки без редактирования.

Имейте модель, которая содержит всю информацию, которая необходима представлению (gui):

/*
 * The model contains the information for the view and information from the view
 * The model is independent of the user interface.
 * It notifies Listener on changes. 
 */
class Model {

    private Listener listener;
    private int x = 0,  y = 0;

    synchronized int getX() {return x;}

    synchronized void setX(int x) { this.x = x; }

    synchronized int getY() {return y;}

    synchronized void setY(int y) { this.y = y; }

    void setListener(Listener listener){
        this.listener = listener;
    }
    //notify listener when changed 
    void notifyListener(){
        if(listener != null) {
            listener.onChange();
        }
    }
}

В этом случае была добавлена ​​синхронизация, чтобы позволить модели использовать потоки.Слушатель определяется следующим образом:

/*
* A simple interface used to link View and Model 
*/
interface Listener {
    void onChange();
}

Представление - только это.Он реализует Listener, поэтому он может прослушивать Model изменения:

/*
 * View is just that: a dumb as possible display 
 */
public class View implements Listener{

    private final JButton button;
    private final MyDrawPanel panel;
    private final Model model;

    public View(Model model) {
        this.model = model;
        panel = new MyDrawPanel();
        button = new JButton("Restart");
        panel.add(button);
    }

    public void go(){
        JFrame frame = new JFrame("Balloon Balls");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300, 300);
        frame.add(panel);
        frame.setVisible(true);
    }

    class MyDrawPanel extends JPanel{
        @Override
        public void paintComponent(Graphics g){
            super.paintComponent(g); //always call super
            g.fillOval(model.getX(), model.getY(), 30, 30);
            g.setColor(Color.BLACK);
        }
    }

    @Override
    public void onChange() {
        panel.repaint();
    }

    void addActionListener(ActionListener listener){
        button.addActionListener(listener);
    }
}

Собрать все вместе: см. Следующее mvce : он добавляет контроллер, который контролирует модель и представление,Для удобства и простоты следующий код можно скопировать в один файл с именем View.java и запустить.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

/*
 * View is just that: a dumb as possible display 
 */
public class View implements Listener{

    private final JButton button;
    private final MyDrawPanel panel;
    private final Model model;

    public View(Model model) {
        this.model = model;
        panel = new MyDrawPanel();
        button = new JButton("Restart");
        panel.add(button);
    }

    public void go(){
        JFrame frame = new JFrame("Balloon Balls");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300, 300);
        frame.add(panel);
        frame.setVisible(true);
    }

    class MyDrawPanel extends JPanel{
        @Override
        public void paintComponent(Graphics g){
            super.paintComponent(g); //always call super
            g.fillOval(model.getX(), model.getY(), 30, 30);
            g.setColor(Color.BLACK);
        }
    }

    @Override
    public void onChange() {
        panel.repaint();
    }

    void addActionListener(ActionListener listener){
        button.addActionListener(listener);
    }

    public static void main(String[]args){
        new Controller();
    }
}

/*
 * A simple interface used to link View and Model 
 */
interface Listener {
    void onChange();
}

/*
 * The model contains the information for the view and information from the view
 * The model is independent of the user interface.
 * It notifies Listener on changes. 
 */
class Model {

    private Listener listener;
    private int x = 0,  y = 0;

    synchronized int getX() {return x;}

    synchronized void setX(int x) { this.x = x; }

    synchronized int getY() {return y;}

    synchronized void setY(int y) { this.y = y; }

    void setListener(Listener listener){
        this.listener = listener;
    }
    //notify listener when changed 
    void notifyListener(){
        if(listener != null) {
            listener.onChange();
        }
    }
}

/*
 * The controller "wires" the view and model, and does the processing.
 */
class Controller implements ActionListener{

    private final Model model;
    private final View view;

    public Controller() {
        model = new Model();
        view = new View(model);
        model.setListener(view);
        view.addActionListener(this);
        view.go();
    }

    @Override
    public void actionPerformed (ActionEvent e){

        new Thread(()->{
            for(int i=0;i<130;i++){
                model.setX(model.getX()+1);
                model.setY(model.getY()+1);
                model.notifyListener();
                System.out.println(model.getX()+" - "+ model.getY());
                try {
                    Thread.sleep(100);
                } catch(Exception ex) { }
            }
        }).start();
    }
}
0 голосов
/ 26 февраля 2019

paintComponent делает именно это, он рисует панель.Первоначально панель закрашивает овал в начале x y.Вы нажимаете кнопку и окно стирается и перекрашивается в новый XY.

Движение - это концепция, которая вам понадобится для обучения компьютеру.Если мы обновляем панель несколько раз в секунду и медленно перемещаем xy, у нас возникает иллюзия движения.

Создайте таймер, который обновляется каждые 10 мс.Каждый раз, когда он обновляется, слегка увеличивайте значения x и y и перекрашивайте панель.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...