Расширение JPanel для рисования изображения. Но на той же панели добавлен JFrame - PullRequest
0 голосов
/ 06 мая 2018

ОБНОВЛЕНИЕ : Как заметил Дмич, поскольку я рисую вне своего класса панели, это приводит к рекурсивным вызовам инициализации моего класса MyPanel и Animation. Итак, как я могу все еще выполнять рисование на MyPanel, в классе анимации, но без этой проблемы.

У меня очень специфическая проблема с моим кодом, и я не знаю, как описать происходящее, но я буду стараться изо всех сил. Я попытался выполнить поиск в stackOverFLow, но проблема в том, что я даже не знаю что искать.

Вот и я:

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

public class MyPanel extends JPanel implements Runnable{
private Animation anim = new Animation();
}
public class Animation extends ??? implements KeyListener{}

Итак, у меня есть класс MyPanel, который расширяет JPanel. Я добавил этот класс в свой JFrame, используя add(new MyPanel()) Я использую тему в MyPanel, которая вызывает @Overrid public void paintComponent(Graphics g) paintComponent вызывает метод в Animation, где я рисую изображение на экране g2d.drawImage(image, int, int, ImageObserver). Проблема в том, чтобы использовать этот метод, мне нужен ImageObserver, который я могу получить, если Animation расширяет JPanel. Но если я расширяю JPanel, на моем JFrame ничего не рисуется, потому что это новая jPanel, которая не добавляется в JFrame.

Но если я расширяю MyPanel (который добавлен в мой JFrame), я получаю целую кучу ошибок.

Ошибки затмения:

Exception in thread "main" java.lang.StackOverflowError
at java.awt.Component.setBackground(Unknown Source)
at javax.swing.JComponent.setBackground(Unknown Source)
at javax.swing.LookAndFeel.installColors(Unknown Source)
at javax.swing.LookAndFeel.installColorsAndFont(Unknown Source)
at javax.swing.plaf.basic.BasicPanelUI.installDefaults(Unknown Source)
at javax.swing.plaf.basic.BasicPanelUI.installUI(Unknown Source)
at javax.swing.JComponent.setUI(Unknown Source)
at javax.swing.JPanel.setUI(Unknown Source)
at javax.swing.JPanel.updateUI(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source) 

Под этим сообщением об ошибке эти 2 строки продолжаются до тех пор, пока не завершатся затмением.

at MyPanel.<init>(MyPanel.java:9)
at Animation.<init>(Animation.java:9)

Любая помощь будет принята с благодарностью.

ОБНОВЛЕНИЕ 2 Добавление примера кода в соответствии с предложением:

import javax.swing.JFrame;

public class Simulation extends JFrame {
private MyPanel panel = new MyPanel();
public Simulation() {
    initUI();
}

private void initUI() {

    add(panel);
    setResizable(false);
    pack();

    setTitle("Simulation");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null); // centers
}
public static void main(String[] args) {

    Simulation ex = new Simulation();
    ex.setVisible(true);
}
}


import javax.swing.*;
import java.awt.*;

public class MyPanel extends JPanel implements Runnable { 
private Animation anim;
public MyPanel() {
    anim = new Animation();
    initPanel();
}

private void initPanel() {
    //other customizables
}

@Override
public void run() {
//Thread that calls repaint();
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2D = (Graphics2D) g;
    anim.step(g2D, this);
}
}


import java.awt.Graphics2D;

public class Animation {
private Sprite player;
private KeyBinder kB;

public Animation() {
    player = new Sprite();
  kB = new KeyBinder(player);
}

public void step(Graphics2D g2d, JPanel p) {
    player.move();
    drawSprite(g2d, p);
}

private void drawSprite(Graphics2D g2d, JPanel p) {
    g2d.drawImage(player.getImage(), player.getX(), player.getY(), p);
}
}

import java.awt.Image;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;

public class Sprite {
private final String PLAYER_IMAGE_FILE = "Image location";
private Image image;
private int width, height;
private int x, y, dx, dy;

public Sprite() {
    x = 0;
    y = 0;
    loadImage();
}

private void loadImage() {
    ImageIcon ii = new ImageIcon(PLAYER_IMAGE_FILE);
    image = ii.getImage();

    width = image.getWidth(null);
    height = image.getHeight(null);
}

public void move() {
    x += dx;
    y += dy;
}

public void keyPressed(KeyEvent e) {

    //assings meaning to keypressed
}

public void keyReleased(KeyEvent e) {

    //assings meaning to keyReleased
}

public int getX() {
    return x;
}

public int getY() {
    return y;
}

public int getW() {
    return width;
}

public int getH() {
    return height;
}

public Image getImage() {
    return image;
}
}

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class KeyBinder extends MyPanel implements KeyListener {
Sprite p;
public KeyBinder(Sprite player) {
    p = player;
    addKeyListener(this);
    setFocusable(true);
    setFocusTraversalKeysEnabled(false);
}

@Override
public void keyPressed(KeyEvent e) {
    p.keyPressed(e);
}
@Override
public void keyReleased(KeyEvent e) {
    p.keyReleased(e);
}
@Override
public void keyTyped(KeyEvent e) {

}
}

Пытаясь отработать код, я внес изменения в свой код, главным образом, пытаясь передать JPanel в метод Animation.step, а также создал отдельный класс для прослушивания клавиш.

Новый список ошибок в основном такой же:

Exception in thread "main" java.lang.StackOverflowError
at java.awt.Component.setBackground(Unknown Source)
at javax.swing.JComponent.setBackground(Unknown Source)
at javax.swing.LookAndFeel.installColors(Unknown Source)
at javax.swing.LookAndFeel.installColorsAndFont(Unknown Source)
at javax.swing.plaf.basic.BasicPanelUI.installDefaults(Unknown Source)
at javax.swing.plaf.basic.BasicPanelUI.installUI(Unknown Source)
at javax.swing.JComponent.setUI(Unknown Source)
at javax.swing.JPanel.setUI(Unknown Source)
at javax.swing.JPanel.updateUI(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source)
at javax.swing.JPanel.<init>(Unknown Source)

Затем бесконечный цикл до завершения следующих трех строк:

at MyPanel.<init>(MyPanel.java:10)
at KeyBinder.<init>(KeyBinder.java:6)
at Animation.<init>(Animation.java:11)

Ответы [ 3 ]

0 голосов
/ 06 мая 2018

Итак, у меня есть класс Panel, который расширяет JPanel. Я добавил этот класс в свой JFrame, используя add (new Panel ()). Я использую Thread в Panel, который вызывает @Overrid public void paintComponent (Graphics g)

Во-первых, Swing НЕ является потокобезопасным, вы не должны обновлять пользовательский интерфейс вне контекста потока диспетчеризации событий.

Во-вторых, НИКОГДА не должно быть ситуации, когда вы бы звонили paintComponent напрямую, и не было бы необходимости, чтобы он был public, поскольку ничто другое не должно вызывать его.

См. Параллельность в Swing для получения более подробной информации

paintComponent вызывает метод в Animation, где я рисую изображение на экране g2d.drawImage (image, int, int, ImageObserver). Проблема в том, чтобы использовать этот метод, мне нужен ImageObserver, который я могу получить, если Animation расширяет JPanel.

Почему бы не сделать Animation "подрисовываемым", предоставляя метод, который требует от вызывающего абонента передавать информацию, необходимую для выполнения вашей работы?

public class Animation {
    public void paint(Graphics2D g2d, ImageObserver observer) {
        //...
    }
}

Вы также можете передать другую информацию, которая может понадобиться методу paint, например модель, которая моделирует текущее состояние.

Это в основном модель делегирования - когда вы «делегируете» ответственность за определенную задачу другому классу

А как же KeyListener?

Я слышал, вы спрашиваете.

Ну, для начала, вероятно, Animation не несет ответственности за его решение. Вместо этого контроллер должен изменять состояние в ответ на событие, которое в конечном итоге раскрашивается средством визуализации

Кроме того, вместо этого следует использовать API привязок клавиш , он решит проблемы ненадежности, связанные с KeyListener

0 голосов
/ 10 мая 2018

Это был еще один случай плохого наследства парней. Я исправил это, реализовав KeyListener в классе MYPanel и передав MyPanel в качестве аргумента объекта Animation. Для меня было плохой практикой пытаться реализовать keyListener для JPanel, но из другого класса.

0 голосов
/ 06 мая 2018

Я бы не рекомендовал расширять класс JPanel, а при создании экземпляра взять параметр JPanel:

public class Panel implements Runnable{
    private JPanel panel;

    public Panel(JPanel panel){
        this.panel = panel;
    }
}

Кроме того, я думаю, что было бы целесообразно сделать класс Panel с компонентом Extend Component таким образом, чтобы иметь бесшовную интеграцию при добавлении его в существующую панель, упомянутую в заголовке:

public class Panel extends Component implements Runnable{
    private JPanel panel;

    public Panel(JPanel panel){
        this.panel = panel;
    }
}

Пожалуйста, дайте мне знать, если это не сработает, или у вас есть другие вопросы по этому поводу!

...