У меня есть программа java 8, в которой Parent - это JFrame с меню, несколькими кнопками, текстовым полем и JTable с фиксированным количеством нередактируемых строк. Количество строк и данных нельзя изменить динамически.
В меню есть список UIManager.getInstalledLookAndFeels()
Изначально видны границы строк JTable
Если LookAndFeel изменить на [Nimbus javax.swing.plaf.nimbus.NimbusLookAndFeel]
, а затем попробовать любой другой LookAndFeel, граница строк исчезнет.
Я использую SwingUtilities.updateComponentTreeUI(parentFrame)
для нанесения LnF. LnF применяется ко всем компонентам, включая JTable, но после применения Nimbus LnF и последующего выбора любого другого LnF границы строк исчезают.
Как вариант repaint()
без разницы.
В изображении
Пожалуйста, предложите.
Образец кода:
package com.sv.runcmd;
import com.sv.core.logger.MyLogger;
import com.sv.swingui.SwingUtils;
import com.sv.swingui.component.AppExitButton;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static com.sv.core.Constants.SP_DASH_SP;
import static com.sv.swingui.UIConstants.EMPTY_BORDER;
public class LnFExample extends JFrame {
public static void main(String[] args) {
new LnFExample().initComponents();
}
private static final String APP_TITLE = "LnF";
private DefaultTableModel model;
private JTable tblCommands;
private JMenuBar mbarSettings;
public LnFExample() {
super(APP_TITLE);
SwingUtilities.invokeLater(this::initComponents);
}
/**
* This method initializes the form.
*/
private void initComponents() {
Container parentContainer = getContentPane();
parentContainer.setLayout(new BorderLayout());
JButton btnExit = new AppExitButton(true);
createTable();
JPanel topPanel = new JPanel(new GridLayout(2, 1));
topPanel.add(btnExit);
topPanel.setBorder(EMPTY_BORDER);
JPanel lowerPanel = new JPanel(new BorderLayout());
JScrollPane jspCmds = new JScrollPane(tblCommands);
lowerPanel.add(jspCmds);
parentContainer.add(topPanel, BorderLayout.NORTH);
parentContainer.add(lowerPanel, BorderLayout.CENTER);
btnExit.addActionListener(evt -> exitForm());
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
exitForm();
}
});
createAppMenu();
setPosition();
}
private final MyLogger logger = MyLogger.createLogger("rc.log");
private void createAppMenu() {
mbarSettings = new JMenuBar();
JMenu menuSettings = new JMenu("Settings");
menuSettings.add(getThemesMenu());
mbarSettings.add(menuSettings);
setJMenuBar(mbarSettings);
}
public UIManager.LookAndFeelInfo[] getAvailableLAFs() {
return UIManager.getInstalledLookAndFeels();
}
public JMenu getThemesMenu() {
JMenu menu = new JMenu("Theme");
int i = 'a';
int x = 0;
for (UIManager.LookAndFeelInfo l : getAvailableLAFs()) {
JMenuItem mi = new JMenuItem((char) i + SP_DASH_SP + l.getName());
if (i <= 'z') {
mi.setMnemonic(i);
}
int finalX = x;
mi.addActionListener(e -> applyTheme(finalX, l));
menu.add(mi);
i++;
x++;
}
return menu;
}
UIManager.LookAndFeelInfo themeToApply;
public void applyTheme(int idx, UIManager.LookAndFeelInfo lnf) {
themeToApply = lnf;
SwingUtilities.invokeLater(this::applyLnF);
}
public void applyLnF() {
try {
UIManager.setLookAndFeel(themeToApply.getClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
SwingUtilities.updateComponentTreeUI(this);
}
private void createTable() {
model = SwingUtils.getTableModel(new String[]{"Col1"});
createRows();
Border borderBlue = new LineBorder(Color.BLUE, 1);
tblCommands = new JTable(model);
}
private void createRows() {
for (int i = 1; i <= 10; i++) {
model.addRow(new String[]{"Row- " + i});
}
}
private void setPosition() {
// Setting to right most position
pack();
GraphicsConfiguration config = getGraphicsConfiguration();
Rectangle bounds = config.getBounds();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);
int x = bounds.x + bounds.width - insets.right - getWidth();
int y = bounds.y + insets.top + 10;
setLocation(x, y);
setVisible(true);
}
/**
* Exit the Application
*/
private void exitForm() {
setVisible(false);
dispose();
logger.dispose();
System.exit(0);
}
}
Кстати, я воспроизвел вашу проблему, когда я перехожу от Nimbus к Metal JTable
границы строк отображаются неправильно, однако с другими L & F этого не происходит, и я рассматриваю это как потенциальную ошибку, однако, пожалуйста, обновите с минимальным воспроизводимым примером как @ camickr сказал, и, возможно, когда вы приложите некоторые усилия, другие тоже
@camickr - я удалил ранее один boz по той же причине, чтобы поместить вокруг него больше информации. Позвольте мне добавить код, было бы здорово, если бы вы удалили -1, потому что эта проблема воспроизводится и может быть полезна для других.
Как сказал @DavidKroukamp, это может быть потенциальная ошибка jdk, о которой я сообщил команде Oracle JDK.
@shaILU Да, определенно похоже на ошибку, убедитесь, что вы установили последнюю версию jdk 8, и если она все еще появляется, подождите, пока Oracle ответит.
Я пробовал последнюю версию jdk 8. Результаты остаются прежними. Спасибо.
Понижение было потому, что нам НЕ нужно повторяться. В последнем вопросе вас попросили опубликовать минимально воспроизводимый пример. Повторение вопроса без «MRE» не улучшает вопрос, поскольку мы не можем проверить изображение, чтобы увидеть, получим ли мы те же результаты. Мы не знаем, используете ли вы, например, пользовательские рендеры, что может вызвать проблему. На этот раз голосование было удалено.
@shaILU, к настоящему времени удалено отрицательное голосование - не беспокойтесь так о отрицательных голосах и задайте правильный вопрос в первый раз. И, кстати, вы до сих пор НЕ разместили минимальный воспроизводимый пример (1-). Вы по-прежнему используете всевозможные классы третьей части, поэтому мы не можем копировать/вставлять/компилировать и тестировать вставленный вами код. Нет необходимости в этих классах. Для создания JTable требуется один оператор: new JTable(3, 3);
. Данные неактуальны. Ваш вопрос касается границ.
Я также получаю те же результаты, используя JDK11 в Windows 10.
Я подозреваю, что проблема связана с интерфейсом UIResource
. Обратите внимание, что это всего лишь интерфейс тегов, реальных методов для реализации нет.
Насколько я понимаю, этот интерфейс должен быть реализован на свойствах компонентов Swing. Например, в свойствах Font, Border, Color, Icon различных компонентов.
Затем, когда вы вызываете SwingUtilities.updateComponentTreeUI(parentFrame)
, все свойства, реализующие UIResource
, будут заменены соответствующим свойством из нового LAF.
Ознакомьтесь с UIManager по умолчанию. В нем будут перечислены все свойства каждого компонента Swing.
Вы увидите, что для большинства LAF свойства находятся в экземпляре FontUIResource
или ColorUIResource
и т. д.
Однако для Nimbus LAF во многих свойствах отсутствует "...UIResource".
Поэтому я бы предположил, что это проблема Nimbus, и я понятия не имею, как ее исправить.
Редактировать:
трудно сказать во время отладки, была ли это проблема Metal LaF ... или проблема Nimbus
Это проблема Nimbus.
Загрузите код по ссылке выше, а затем внесите следующие изменения:
SwingUtilities.updateComponentTreeUI( rootPane );
System.out.println(table.getDefaultRenderer(Object.class));
Теперь переключите LAF между не NImbus, и вы увидите, что средство визуализации таблицы по умолчанию для не Nimbus LAF содержит UIResource в имени класса, что указывает мне на то, что они реализуют интерфейс UIResource.
Теперь переключитесь на Nimbus LAF. Средство визуализации не имеет UIResource в имени класса.
Теперь переключитесь обратно на любой другой LAF, и рендеринг будет неправильным, потому что средство визуализации Nimbus не было заменено надлежащим средством визуализации LAF.
Обратите внимание, что это не проблема LAF. Он предназначен для работы таким образом. Это позволяет вам создать собственный рендерер, который можно использовать во всех LAF (если, конечно, вы не пометите рендерер интерфейсом UIResource).
По какой-то причине разработчики Nimbus, похоже, не пометили рендереры интерфейсом UIResource, поэтому, как только они установлены, они не изменяются с остальной частью LAF.
Таким образом, другим решением было бы создание средства рендеринга «оболочки», которое просто обертывает средство рендеринга по умолчанию и вызывает его логику рендеринга по умолчанию, но также реализует интерфейс UIResource. Затем вам нужно будет заменить каждый рендерер Nimbus по умолчанию на рендерер-оболочку.
Хорошее объяснение, хотя во время отладки было трудно сказать, была ли это проблема Metal LaF, т.е. она неправильно заменяет значения по умолчанию таблицы или проблема Nimbus, т.е. она делает что-то странное с визуализатором ячеек таблицы (он использует синтетический LaF для этого для это из исходного кода). Я мог бы исправить это, просто переопределив метод prepareRenderer
метода JTable
и установив границу на MetalBorders.TableHeaderBorder
, но да, это своего рода хак, потому что вам нужно будет отслеживать, был ли это первый раз, когда металл применялся или нет, иначе границы удвоятся. . +1.
@shaILU Проверьте мой ответ на предмет потенциального обходного пути
@DavidKroukamp Я считаю, что это проблема Nimbus (не Metal). См. редактирование.
Хороший друг!!
Я считаю, что после того, как эффект Nimbus повлияет на Windows и WindowsClassic LnF, я соответствующим образом изменю код.
@shaILU, вот что говорится в моем ответе. В нем говорится, что рендереры Nimbus не заменены. Таким образом, они будут использоваться любым другим LAF, на который вы переключитесь. Правильное решение — обернуть рендереры Nimbus. Переопределение средства визуализации prepareRender(...)
не заменяет соответствующие средства визуализации для LAF.
Как обсуждалось в комментариях и @camickr, это, вероятно, ошибка с Nimbus или Metal LaF.
Однако я сделал обходной путь, который вы можете использовать сейчас.
По сути, я переопределяю prepareRenderer
из JTable
и проверяю, используется ли Metal LaF (и что он не был установлен при запуске, иначе он нарисует 2 границы вокруг JTable
), и если эти условия соблюдены, мы просто устанавливаем границу для каждой строки на new MetalBorders.TableHeaderBorder()
:
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
JComponent component = (JComponent) super.prepareRenderer(renderer, row, column);
if (UIManager.getLookAndFeel().getName().equals("Metal") && !wasMetalOnStartup) {
component.setBorder(new MetalBorders.TableHeaderBorder());
}
return component;
}
TestApp.java:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.UUID;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.metal.MetalBorders;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.metal.OceanTheme;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
public class TestApp {
private JTable table;
private boolean wasMetalOnStartup = false;
public TestApp() {
setNimbusLookAndFeel();
initComponents();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(TestApp::new);
}
private void initComponents() {
wasMetalOnStartup = UIManager.getLookAndFeel().getName().equals("Metal");
JFrame frame = new JFrame("TestApp");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
// setup chmahe LaF button
JButton refreshButton = new JButton("Change L&F");
refreshButton.addActionListener((ActionEvent e) -> {
try {
if (!UIManager.getLookAndFeel().getName().equals("Nimbus")) {
setNimbusLookAndFeel();
} else {
setMetalLookAndFeel();
}
SwingUtilities.updateComponentTreeUI(frame);
} catch (Exception ex) {
}
});
table = new JTable() {
private static final long serialVersionUID = 1L;
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
JComponent component = (JComponent) super.prepareRenderer(renderer, row, column);
if (UIManager.getLookAndFeel().getName().equals("Metal") && !wasMetalOnStartup) {
component.setBorder(new MetalBorders.TableHeaderBorder());
}
return component;
}
};
// setup JTable and custom table model with intial data
Object[][] data = getRandomData();
String[] columnNames = {"Random Data"};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
table.setModel(model);
table.getColumnModel().getColumn(0).setPreferredWidth(300);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
// add components to the panel
JScrollPane pane = new JScrollPane(table);
panel.add(pane, BorderLayout.CENTER);
panel.add(refreshButton, BorderLayout.SOUTH);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
private void setNimbusLookAndFeel() {
try {
UIManager.setLookAndFeel(new NimbusLookAndFeel());
wasMetalOnStartup = false;
} catch (Exception ex) {
}
}
private void setMetalLookAndFeel() {
try {
MetalLookAndFeel.setCurrentTheme(new OceanTheme());
UIManager.setLookAndFeel(new MetalLookAndFeel());
wasMetalOnStartup = false;
} catch (Exception ex) {
}
}
private Object[][] getRandomData() {
Object[][] data = {{UUID.randomUUID()}, {UUID.randomUUID()}, {UUID.randomUUID()}, {UUID.randomUUID()}};
return data;
}
}
(1+), но я бы посоветовал вам использовать: UIManager.getBorder("TableHeader.cellBorder")
чтобы сбросить его для любого LAF, отличного от Nimbus.
Спасибо, Дэвид и @camickr. Это сработало для меня, и я принял ответ. Поскольку это было, и вы оба, возможно, уже удалили отрицательный голос, если нет, пожалуйста, сделайте это (я вижу еще один отрицательный голос). Буду иметь в виду про МРЭ. Спасибо вам обоим.
Рад помочь. И я никогда не минусовал. Я проголосовал за и отозвал свой закрытый голос. Если мои ответы и ответы @camickrs помогли, возможно, подумайте и о том, чтобы проголосовать и за них.
Получил ответ по электронной почте на ошибку, которую я поднял от Oracle по этой проблеме, и они также смогли воспроизвести ее как ошибку.
JDK-8258567.
Ссылка: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8258567
(1-) Этот вопрос был задан ранее: (stackoverflow.com/questions/65254253/… ) и удален. Предложение состояло в том, чтобы предоставить минимальный воспроизводимый пример. Я до сих пор не вижу ни одного.