JTable в JPanel показывает все свои значения только при прокрутке по нему или с помощью showing-hidding-отображение JPanel во время выполнения вручную

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