#java #swing #intellij-idea
#java #swing #intellij-idea
Вопрос:
Я работаю в настольной игре Java, где я пытаюсь отобразить некоторые панели, состоящие из a JTable
с a JScrollPane
, которые имеют только один столбец и несколько строк, содержащих данные, которые я хочу отобразить. Эти панели не видны при запуске приложения, потому что они отображаются в зависимости от JButton
нажатия.
Когда я запускаю свое приложение, эти панели уже добавлены во фрейм, но не видны. На данный момент я пытаюсь отобразить их, нажав соответствующую кнопку, они отображаются, но видны не все строки. Если я снова нажму кнопку, панель будет скрыта, а если я снова нажму кнопку, чтобы отобразить ее снова, теперь она отображается со всеми видимыми строками.
По какой-то причине, просто установив для его видимости значение true -> false -> true нажатием соответствующей кнопки, JTable
на панели отображаются все ее строки, но когда я показываю ее в первый раз, это не так… Мне нужно сделать это дважды.
Чтобы проверить это, я пытаюсь установить видимость панели в true -> false -> true при программном запуске приложения, но это не работает. Это работает, только если я нажимаю кнопку, которая отображает панель (помните, дважды, в первый раз, когда она не показывает все строки, во второй раз, когда она показывает). Кроме того, я проверил путем отладки, если на данный момент я установил для видимости панели значение true в первый раз, в ней есть все данные внутри, и это правильно, так что это что-то просто графическое, потому что данные есть. Также я понял, что при прокрутке с помощью мыши при первом показе панели отображаются все данные, поэтому я предполагаю, что это должно что-то обновлять при прокрутке, или когда я показываю панель во второй раз, я нажимаю соответствующую кнопку.
Я пробовал revalidate()
и repaint()
таблицу, fireTableDataChanged()
и fireTableStructureChanged()
модель таблицы. Также я создал поток, который запускается одновременно с запуском приложения, который устанавливает видимость этих панелей в true и false в начале и повторно проверяет таблицы, просто чтобы попытаться увидеть, что может происходить при отображении / скрытии панелей.
После создания этого потока я понял, что если я с самого начала установлю для видимости этих панелей значение true, я увижу, как поток пользовательского интерфейса каким-то образом «обновляет» панели, поскольку я вижу их незавершенными, а после завершения — за долю секунды. Но после этого мне приходится закрывать их вручную, потому что я не могу контролировать (я пытался) время, необходимое для их заполнения, прежде чем закрывать их.
Вот изображение панели, когда я setVisible(true)
использую одну из этих панелей:
И вот изображение той же панели, когда я скрываю и показываю ее снова (вручную), без выполнения каких-либо других действий:
Вот код модели таблицы, заданной в JTable панели:
/**
* Class to create the model instance for the selector panel list
*/
public class CustomTableModelForPredefinedMessagesSelectorKeyboard extends DefaultTableModel {
private ArrayList selectedList = new ArrayList<>();
public CustomTableModelForPredefinedMessagesSelectorKeyboard() {
this.addColumn(LanguageConfiguration.getLanguageSelected().getString("selection"));
//setListType(ListType.PREDEFINED_MESSAGES);
setListType();
}
/**
* Function to get the size of the list
*
* @return the list size
*/
//@Override
public int getSize() {
return selectedList.size();
}
/**
* Function to get the element of a specific index from the list
*
* @param index index of the message to be retrieved
* @return the message text of the corresponding index
*/
public Object getElementAt(int index) {
return selectedList.get(index).toString();
}
public ArrayList getSelectedList() {
return selectedList;
}
public void setSelectedList(ArrayList selectedList) {
this.selectedList = selectedList;
}
/**
* Function to set the list of the selector panel according to the list type selected
*/
@SuppressWarnings("unchecked")
public void setListType() {
selectedList.add("selectorPanel_PredefMsg_EnemyOnSight");
selectedList.add("selectorPanel_PredefMsg_GreenAlert");
selectedList.add("selectorPanel_PredefMsg_HowAreYou");
selectedList.add("selectorPanel_PredefMsg_InminentAttack");
selectedList.add("selectorPanel_PredefMsg_No");
selectedList.add("selectorPanel_PredefMsg_Positioned");
selectedList.add("selectorPanel_PredefMsg_Ready");
selectedList.add("selectorPanel_PredefMsg_RedAlert");
selectedList.add("selectorPanel_PredefMsg_ReturnToBase");
selectedList.add("selectorPanel_PredefMsg_TargetOutOfReach");
selectedList.add("selectorPanel_PredefMsg_WaitingOrders");
selectedList.add("selectorPanel_PredefMsg_WeHaveAProblem");
selectedList.add("selectorPanel_PredefMsg_YellowAlert");
selectedList.add("selectorPanel_PredefMsg_Yes");
for (Object string : selectedList)
this.addRow(new Object[]{LanguageConfiguration.getLanguageSelected().getString((String) string)});
if (SwingInterfaceSubJPanels.getPredefinedMessagesSelectorJPanel() != null)
for (Component component : SwingInterfaceSubJPanels.getPredefinedMessagesSelectorJPanel().getComponents())
if (component instanceof JScrollPane) {
JScrollPane messagesListScrollPane = (JScrollPane) component;
JViewport viewport = messagesListScrollPane.getViewport();
preselectFirstElement((CustomJTable) viewport.getView());
break;
}
}
/**
* Function to pre-select the firs item of the list in the selector list
*
* @param customJTable the table where the selection will be performed
*/
private void preselectFirstElement(CustomJTable customJTable) {
if (customJTable.getRowCount() > 0)
customJTable.getSelectionModel().setSelectionInterval(0, 0);
}
}
И вот код панели:
/**
* Class to create and manage a selector keyboard
*/
public class PredefinedMessagesSelectorKeyboard extends JPanel {
/**
* Color of shadow
*/
private final Color shadowColor;
/**
* Double values for Horizontal and Vertical radius of corner arcs
*/
private final Dimension arcs;
/**
* Variables to get the coordinates of the mouse on mouse click event over the jpanel
*/
private int xJPanelCoord, yJPanelCoord;
Component componentToManage;
SelectorConfirmationController.Type selectorPanelType;
/**
* Constructor to create the selector panel structure and add its components
*/
public PredefinedMessagesSelectorKeyboard() {
DisplayMode displayMode = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode();
double screenWidth = displayMode.getWidth();
double screenHeight = displayMode.getHeight();
this.setPreferredSize(new Dimension((int) Math.round(screenWidth * 0.35), (int) Math.round(screenHeight * 0.6)));
//this.setBackground(new java.awt.Color(5, 122, 122));
this.setBackground(new Color(150, 150, 150));
addSelectorPanelComponents();
setVisible(false);
setOpaque(false);
makeJPanelDraggable();
shadowColor = Color.black;
arcs = new Dimension(100, 100);
}
/**
* Function to add the components of the JPanel
*/
private void addSelectorPanelComponents() {
this.setLayout(new GridBagLayout());
addSelectorPanelJTable();
addSelectorPanelButtons();
addSelectorPanelCloseJButton();
}
/**
* Function to add the panel with the elements available for selection
*/
private void addSelectorPanelJTable() {
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.gridheight = 2;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.fill = GridBagConstraints.BOTH;
gridBagConstraints.insets = new Insets(40, 20, 20, 0);
CustomTableModelForPredefinedMessagesSelectorKeyboard customTableModelForPredefinedMessagesSelectorPanelList = new CustomTableModelForPredefinedMessagesSelectorKeyboard();
CustomJTable predefinedMessagesselectorPanelJTable = new CustomJTable(customTableModelForPredefinedMessagesSelectorPanelList);
predefinedMessagesselectorPanelJTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
predefinedMessagesselectorPanelJTable.setName("predefinedMessagesSelectorPanelJTable");
JScrollPane selectorJTableJScrollPane = new JScrollPane(predefinedMessagesselectorPanelJTable);
selectorJTableJScrollPane.getViewport().setBackground(new Color(24, 27, 34));
add(selectorJTableJScrollPane, gridBagConstraints);
}
/**
* Function to add the close panel button and the selection confirmation button
*/
private void addSelectorPanelButtons() {
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 4;
gridBagConstraints.gridy = 1;
gridBagConstraints.insets = new Insets(20, 20, 0, 20);
JButton selectPreviousButton = createScaledSwingShapedButton("/images/boton_popup_panel_selector_anterior.png",
"/images/boton_popup_panel_selector_anterior_seleccionado.png");
selectPreviousButton.setName("selectorPanelPreviousButton");
selectPreviousButton.setToolTipText(LanguageConfiguration.getLanguageSelected().getString("tooltipTextPreviousButton"));
selectPreviousButton.addActionListener(e -> {
for (Component component : getComponents())
if (component instanceof JScrollPane) {
JScrollPane messagesListScrollPane = (JScrollPane) component;
JViewport viewport = messagesListScrollPane.getViewport();
selectPrevious((CustomJTable) viewport.getView());
break;
}
});
JButton confirmSelectedButton = createScaledSwingShapedButton("/images/boton_popup_panel_selector_confirmar.png",
"/images/boton_popup_panel_selector_confirmar_seleccionado.png");
confirmSelectedButton.setName("selectorPanelConfirmButton");
confirmSelectedButton.setToolTipText(LanguageConfiguration.getLanguageSelected().getString("tooltipTextConfirmButton"));
confirmSelectedButton.addActionListener(new SelectorConfirmationController(componentToManage, selectorPanelType));
JButton selectNextButton = createScaledSwingShapedButton("/images/boton_popup_panel_selector_siguiente.png",
"/images/boton_popup_panel_selector_siguiente_seleccionado.png");
selectNextButton.setName("selectorPanelNextButton");
selectNextButton.setToolTipText(LanguageConfiguration.getLanguageSelected().getString("tooltipTextNextButton"));
selectNextButton.addActionListener(e -> {
for (Component component : getComponents()){
if (component instanceof JScrollPane){
JScrollPane messagesListScrollPane = (JScrollPane) component;
JViewport viewport = messagesListScrollPane.getViewport();
selectNext((CustomJTable) viewport.getView());
break;
}
}
});
JPanel lateralButtonsPanel = new JPanel();
lateralButtonsPanel.setLayout(new BoxLayout(lateralButtonsPanel, BoxLayout.Y_AXIS));
lateralButtonsPanel.setOpaque(false);
lateralButtonsPanel.setName("lateralButtonsPanel");
lateralButtonsPanel.add(selectPreviousButton);
lateralButtonsPanel.add(confirmSelectedButton);
lateralButtonsPanel.add(selectNextButton);
add(lateralButtonsPanel, gridBagConstraints);
}
/**
* Function to create and add the close panel button to the panel
*/
private void addSelectorPanelCloseJButton() {
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 4;
gridBagConstraints.gridy = 0;
//gridBagConstraints.anchor = GridBagConstraints.NORTHEAST;
gridBagConstraints.insets = new Insets(20, 20, 0, 20);
JButton closeMessagesConsoleButton = createScaledSwingShapedButton("/images/boton_consola_mensajes_2.png",
"/images/boton_consola_mensajes_2_seleccionado.png");
closeMessagesConsoleButton.setName("selectorPanelCloseButton");
closeMessagesConsoleButton.setToolTipText(LanguageConfiguration.getLanguageSelected().getString("tooltipTextClosePanelButton"));
//closeMessagesConsoleButton.addActionListener(e -> setVisible(false));
closeMessagesConsoleButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
/*if (getComponentToManage().getName().equals("messageBoxTable"))*/
SwingInterfaceSubJPanels.getMainScreenUpperInformationLabel().setVisible(false);
setVisible(false);
}
});
add(closeMessagesConsoleButton, gridBagConstraints);
}
/**
* Function to select the next item of the list in the selector list
* @param customJTable the table where the selection will be performed
*/
private void selectPrevious(CustomJTable customJTable){
if (customJTable.getSelectedRow() > 0) {
customJTable.getSelectionModel().setSelectionInterval(customJTable.getSelectedRow() - 1, customJTable.getSelectedRow() - 1);
customJTable.scrollRectToVisible(new Rectangle(customJTable.getCellRect(customJTable.getSelectedRow()-1, 0, true)));
}
}
/**
* Function to select the next item of the list in the selector list
* @param customJTable the table where the selection will be performed
*/
private void selectNext(CustomJTable customJTable){
if (customJTable.getSelectedRow() >= 0 amp;amp; customJTable.getSelectedRow() < customJTable.getRowCount()-1) {
customJTable.getSelectionModel().setSelectionInterval(customJTable.getSelectedRow() 1, customJTable.getSelectedRow() 1);
customJTable.scrollRectToVisible(new Rectangle(customJTable.getCellRect(customJTable.getSelectedRow() 1, 0, true)));
}
}
public Component getComponentToManage() {
return componentToManage;
}
public void setComponentToManage(Component componentToManage) {
this.componentToManage = componentToManage;
}
public SelectorConfirmationController.Type getSelectorPanelType() {
return selectorPanelType;
}
public void setSelectorPanelType(SelectorConfirmationController.Type selectorPanelType) {
this.selectorPanelType = selectorPanelType;
}
/**
* Function to create and return a scaled swing shaped button with its corresponding images
*
* @param buttonImage button image when the button is not pressed
* @param buttonPressedImage button image when the button is pressed
* @return the button already built
*/
private JButton createScaledSwingShapedButton(String buttonImage, String buttonPressedImage) {
Image scaledImage;
try {
SwingShapedButton swingShapedButton = new SwingShapedButton();
JButton newButton = (JButton) swingShapedButton.createComponent(SwingShapedButton.ButtonShape.ROUND);
Image imageButton = ImageIO.read(getClass().getResource(buttonImage));
scaledImage = scaleImage(imageButton);
newButton.setIcon(new ImageIcon(scaledImage));
Image imageButtonPressed = ImageIO.read(getClass().getResource(buttonPressedImage));
scaledImage = scaleImage(imageButtonPressed);
newButton.setPressedIcon(new ImageIcon(scaledImage));
return newButton;
} catch (IllegalArgumentException | IOException e) {
e.printStackTrace();
DataBase.database.insertSystemErrorLog(Thread.currentThread().getStackTrace()[1].getMethodName(), e);
return (JButton) new SwingShapedButton().createComponent(SwingShapedButton.ButtonShape.ROUND);
}
}
/**
* Function to scale the images of the buttons according to the screen resolution
*
* @param imageButton the image to be set to the button
* @return the image resized
*/
private Image scaleImage(Image imageButton) {
return imageButton.getScaledInstance((int) Math.round(((BufferedImage) imageButton).getWidth() * InterfaceControlsManager.getWidth_percentage()),
(int) Math.round(((BufferedImage) imageButton).getHeight() * InterfaceControlsManager.getHeight_percentage()),
Image.SCALE_SMOOTH);
}
/**
* Function to allow the panel to be draggable over the frame
*/
private void makeJPanelDraggable() {
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
xJPanelCoord = e.getX();
yJPanelCoord = e.getY();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
int x = e.getXOnScreen() - xJPanelCoord;
int y = e.getYOnScreen() - yJPanelCoord;
setLocation(x, y);
}
});
}
/**
* Function to set the appearance of the panel
*
* @param g the graphics to be set
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
/*
Distance between shadow border and opaque panel border
*/
int shadowGap = 5;
int shadowAlpha = 10;
Color shadowColorA = new Color(shadowColor.getRed(),
shadowColor.getGreen(), shadowColor.getBlue(), shadowAlpha);
Graphics2D graphics = (Graphics2D) g;
//Sets antialiasing if HQ.
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//Draws shadow borders if any.
int strokeSize = 1;
boolean shady = true;
graphics.setColor(shadowColorA);
int shadowOffset = 4;
graphics.fillRoundRect(
shadowOffset,// X position
shadowOffset,// Y position
width - strokeSize - shadowOffset, // width
height - strokeSize - shadowOffset, // height
arcs.width, arcs.height);// arc Dimension
//Draws the rounded opaque panel with borders.
graphics.setColor(getBackground());
graphics.fillRoundRect(0, 0, width - shadowGap,
height - shadowGap, arcs.width, arcs.height);
graphics.setColor(getForeground());
graphics.setStroke(new BasicStroke(strokeSize));
graphics.drawRoundRect(0, 0, width - shadowGap,
height - shadowGap, arcs.width, arcs.height);
//Sets strokes to default, is better.
graphics.setStroke(new BasicStroke());
}
}
Может кто-нибудь объяснить мне, почему я вижу этот эффект на панели? Есть какая-то причина, по которой JTable не отображает свои данные при первом запуске setVisible(true)
панели, но во второй раз отображается правильно? Может быть что-то с пользовательским интерфейсом, или, может быть, я чего-то здесь не понимаю?
Спасибо за любой комментарий, который вы можете мне дать!
Комментарии:
1. Вы случайно не сталкиваетесь с исключением в вашем потоке awt при первом открытии таблицы? Например, указатель Null, это может прервать рендеринг таблицы.
2. @SnowmanXL нет, я этого не делал 🙁 … Я не получаю никаких исключений ни при выполнении приложения, ни при открытии таблицы. Вот почему я схожу с ума от этого
3. эти панели уже добавлены во фрейм, но не видны. — не знаю, что это значит. Если панели занимают одно и то же место на фрейме, вам следует либо 1) использовать CardLayout для управления с помощью panel is visible, либо 2) Добавить панель в окно просмотра панели прокрутки, когда вы хотите поменять панели местами. Панели не должны просто находиться на невидимом фрейме. * Также я создал поток * — все компоненты Swing должны быть созданы и обновлены в потоке отправки событий (EDT). Вы не должны использовать отдельный поток для изменения видимости компонентов.
4. Мне не нравится дизайн вашей табличной модели. Похоже, у вас есть две копии данных. Вы вызываете addRow(…), чтобы добавить данные в DefaultTableModel, но затем сохраняете копию в своем классе модели. Данные должны быть только в одном месте. Метод не нужен
getSize()
. Этот метод реализован в DefaultTableModel .getSelectedList()
Метод должен получать данные, используяgetValueAt(...)
метод в цикле. Вы НЕ должны ссылаться на JScrollPane в TableModel. Табличная модель хранит только данные и ничего не должна знать о контейнере, в который добавляется JTable.5. @camickr спасибо за ваши советы! Что касается панелей, да, я создаю их все и добавляю их во фрейм в том же месте, но одновременно видна только одна, поэтому у меня нет никаких проблем с их отображением и правильным отображением в их местоположении. Они полностью независимы, поэтому я не могу использовать CardLayout, так как мне не нужны ссылки на другие панели, когда я показываю одну из них. Почему вы думаете, что не стоит делать их невидимыми при запуске приложения? Что касается темы, это было просто для тестирования некоторых вещей, поэтому я уже удалил из проекта