Неправильная JPanel отображается в CardLayout. Проблемы с getGraphics()

У меня есть класс графического интерфейса верхнего уровня под названием SpacePotaters (подкласс JFrame), и я добавляю JPanel с именем panelViews (который использует менеджер макетов CardLayout) на его панель содержимого. Помимо прочего, я добавляю объект MainMenu (подкласс JPanel) и объект GameView (подкласс JPanel) в качестве карт для PanelView.

public SpacePotaters(){
    super("Space Potaters");
    Container content = getContentPane();

    // initialize components
    gameView = new GameView(this);
    mainMenu = new MainMenu(this);
    leaderboard = new Leaderboard(this);
    instructions = new JPanel(); // to do
    cLayout = new CardLayout();

    // add "cards" to CardLayout manager
    panelViews = new JPanel(cLayout);
    panelViews.setPreferredSize(new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT));
    panelViews.add("gameView", gameView);
    panelViews.add("mainMenu", mainMenu);
    panelViews.add("leaderboard", leaderboard);
    panelViews.add("instructions", instructions);

    // initially display menu menu
    content.add(panelViews);
    cLayout.show(panelViews,"mainMenu");

    addWindowListener(this);
    pack();
    setResizable(false);

    // relocate window to center of screen
    setLocationRelativeTo(getRootPane());
}

В случае, если это полезно, метод рисования главного меню здесь:

@Override
public void paintComponent(Graphics g){
    super.paintComponent(g);
    g.drawImage(mainMenuBackground, 0, 0, spTop.getWindowWidth(), spTop.getWindowHeight(), null);

}

Прямо сейчас я просто рисую фоновое изображение - позже я добавлю JButtons, которые при нажатии будут переключаться на разные карты.

Идея в том, что я начинаю с карты главного меню и при необходимости переключаюсь на другие карты. Я запустил программу и заметил, что карта главного меню кратковременно мигает, прежде чем заменить изображение, созданное для gameView. Я знаю, что мне нужно подождать, чтобы отобразить графику gameView, пока я фактически не переключусь на эту карту, чтобы игра не запустилась преждевременно. Это не то, где я запутался. Даже если я не жду, не должен ли GameView просто начать работать без возможности его просмотра? Другими словами, не должно ли mainMenu оставаться единственной видимой картой независимо от того, что происходит внутри gameView?

Я подумал, что проблема была в GameView, который использует активный рендеринг и двойную буферизацию. GameView имеет этот метод, который вызывается один раз в каждом цикле игры:

public void paintScreen(){
    // get fresh graphics context for GameView each time
    Graphics context = getGraphics();
    if (context != null && dbImage != null){
        context.drawImage(dbImage, 0, 0, null);
    }
    context.dispose();
}

Закомментирование тела этого метода дает желаемые результаты - отображается только главное меню. Я не понимаю, почему это так. Я думал, что каждый компонент (JFrame, JPanel и т. Д.) Имеет свой собственный графический контекст. Поэтому, когда я вызываю getGraphics() внутри GameView, не должен ли он возвращать графический контекст для панели gameView, а не графический контекст для spTop или panelViews? А поскольку он изменяет только этот конкретный графический контекст, если панель gameView не видна (чего не должно быть, если я использую метод show() CardLayout для объекта mainMenu), то это не должно влиять на мой вид главного меню., Есть идеи?

Примечание: если я протестирую gameView и mainMenu с помощью isVisible(), gameView возвращает false, а mainMenu возвращает true. Кроме того, я использовал getGraphics(), чтобы проверить, были ли контексты графики mainMenu, spTop и gameView разными - они были. Таким образом, gameView рисует изображение в своем собственном графическом контексте и не отображается, но эффекты drawImage() в paintScreen() все еще показывают! Действительно смущен.

1 ответ

Потратив слишком много времени, пытаясь выяснить, что происходит, я могу только заключить, что активный рендеринг во встроенном графическом компоненте (по крайней мере, в той форме, в которой я его использую) игнорирует видимость этого компонента. Поэтому drawImage() внутри paintScreen() всегда будет отображать видимые результаты, независимо от того, является ли JPanel, из которого она вызывается, видимым или нет.

Я не совсем уверен, но я думаю, что эта путаница может иметь отношение к попытке смешать активный рендеринг с пассивным рендерингом. MainMenu переопределяет paintComponent() и поэтому использует пассивный рендеринг для рисования самого себя. Может быть, установив видимость моего gameView в false, JFrame верхнего уровня просто не вызывает gameView's paintComponent(). Однако, поскольку gameView рисует с помощью paintScreen(), установка его видимости в false не влияет на его поведение. Это все предположения, основанные на экспериментах, так как я не мог найти никакой конкретной информации об этом.

Кроме того, я не уверен, что получил бы те же результаты, если бы попытался выполнить активный рендеринг с использованием BufferStrategy (т. Е. Если бы BufferStrategy show() также игнорировал бы видимость связанного с ним графического компонента и рисовал на видимом экране). также). Это то, что еще нужно исследовать.

Что касается того, как я собираюсь согласовать это понимание с моей текущей программой - поскольку я не хочу переписывать все, я решил использовать гибридный подход. Я собираюсь использовать пассивный рендеринг для переключения между панельными панелями / "картами" (используя CardLayout). Только когда я переключаюсь на gameView, я включаю его режим активного рендеринга. Когда я выхожу и возвращаюсь на другую карту, я отключаю активный рендеринг gameView. Я знаю, что смешивать пассивный и активный рендеринг не рекомендуется, но, надеюсь, никаких проблем не возникает, поскольку я никогда не использую обе стратегии одновременно. Этот подход в настоящее время работает для переключения между mainMenu и gameView, поэтому я ожидаю, что он будет работать, когда я добавлю другие пассивно визуализированные компоненты.

Другие вопросы по тегам