Неправильная 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, поэтому я ожидаю, что он будет работать, когда я добавлю другие пассивно визуализированные компоненты.