JButton, непосредственно нарисованный на панели, не показывает фон с помощью WindowsLookAndFeel

Итак, я разрабатываю визуальный дизайнер в стиле перетаскивания на Java с использованием Swing.

Поскольку компоненты, которые вы видите в дизайнере, не являются фактическими компонентами, а являются только визуальным представлением (например, кнопки нельзя нажимать, текстовые поля не могут принимать текст и т. д.), я рисую их прямо на своей панели, переопределяя методы getX(), getY(), getWidth() и getHeight().

Он отлично работает на любом LookAndFeel, кроме WindowsLookAndFeel. Там почему-то не рисуются фоны JButtons. Мне приходится использовать Windows LookAndFeel для дизайнера, потому что именно так развертывается наше программное обеспечение, и если я использую другой LookAndFeel, макет дизайнера не будет таким же, как наше программное обеспечение.

Я потерял несколько часов, пытаясь решить эту проблему, и я подозреваю, что это происходит потому, что, не добавляя JButton в контейнер, его собственный одноранговый узел не был создан. Я пытался создать пир вручную, но безуспешно.

Я заметил эту проблему в Windows 7 (с Aero) и Windows 8. Если я использую классическую тему в Windows 7, она работает, поэтому я предполагаю, что она также работает в Windows XP (но я не проверял это). Я использую Java 1.7.0_25-b17, чтобы проверить это.

Вот пример, который я сделал, чтобы показать проблему:

package test;


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestSwing {

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { }

        JFrame f = new JFrame();
        f.setSize(300, 150);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        f.getContentPane().setLayout(new BoxLayout(f.getContentPane(), BoxLayout.X_AXIS));
        JPanel p1 = new JPanel();
        p1.setPreferredSize(new Dimension(150, 150));
        p1.setBorder(BorderFactory.createLineBorder(Color.yellow));
        p1.add(new JButton("Test"));
        f.add(p1);

        final MyButton b = new MyButton();
        b.addNotify();

        JPanel p2 = new JPanel() {
            @Override   
            public void paint(Graphics g) {
                super.paint(g);
                b.paint(g);
            }
        };
        p2.setPreferredSize(new Dimension(150, 150));
        p2.setBorder(BorderFactory.createLineBorder(Color.red));

        f.add(p2);
        f.setVisible(true);
    }

    private static class MyButton extends JButton {
        public MyButton() {
            super("Test");
        }

        @Override
        public int getWidth() {
            return 70;
        }
        @Override
        public int getHeight() {
            return 25;
        }

    }
}

Вот пример использования WindowsLookAndFeel:
введите здесь описание изображения

А вот с MetalLookAndFeel:
введите здесь описание изображения


person Jouwee    schedule 07.03.2014    source источник
comment
О, хорошая идея. Вот пример с использованием WindowsLookAndFeel: i.imgur.com/KrJgeKb.png А здесь с MetalLookAndFeel: i.imgur.com/XU2HK9q.png   -  person Jouwee    schedule 08.03.2014
comment
Сделанный. Обратите внимание, что когда я попробовал ваш код с Windows L&F, он отлично работал в Windows 7.   -  person Hovercraft Full Of Eels    schedule 08.03.2014
comment
Спасибо. Вот интересно, я проверил это на своем рабочем месте на Windows 7, завтра попробую еще раз. Может быть, это как-то связано с версией Java? Какой из них вы используете?   -  person Jouwee    schedule 08.03.2014
comment
Теперь я использую Eclipse с Java 7, но по какой-то причине (ничего общего с вашей программой) я могу работать только с совместимостью с Java 6.   -  person Hovercraft Full Of Eels    schedule 08.03.2014
comment
Изменить: я только что запустил его из командной строки, используя версию Java 1.7.0_03. Опять же, нет проблем.   -  person Hovercraft Full Of Eels    schedule 08.03.2014
comment
Хорошо, я запустил его из командной строки, просто чтобы убедиться, что это не NetBeans испортил его, но проблема осталась. Я попытаюсь получить java 1.7.0_03, чтобы убедиться, что это проблема версии, и я также попробую еще раз на Windows 7 (хотя этот тест придется подождать, потому что в моем распоряжении нет Windows 7 )   -  person Jouwee    schedule 08.03.2014
comment
Я использую java version "1.7.0_10 в Windows 7. У меня та же проблема, что и у вас. Так что это похоже на проблему версии.   -  person camickr    schedule 08.03.2014
comment
Я разрабатываю визуальный конструктор в стиле перетаскивания на Java с использованием Swing. Вы думали о том, чтобы использовать свои способности .. во благо? Миру не нужен еще один (проклятый) D-n-D GUI-дизайнер.   -  person Andrew Thompson    schedule 08.03.2014
comment
@AndrewThompson Я разрабатываю его с нуля, потому что у нас есть более 8 миллионов строк устаревшего кода на Cobol, более 5000 окон, созданных с помощью нашей собственной среды, и для этого нет визуального редактора, поэтому мы должны сделать свой собственный. Кроме того, я согласен с вами, если бы мы делали программное обеспечение с нуля, мы бы определенно использовали существующий фреймворк/редактор.   -  person Jouwee    schedule 08.03.2014
comment
правильно, любой, кто кодирует в Cobol или RPG, может создать собственный DND на Java с нуля, конечно, нет проблем, собственный DND проще :-), вам нужно переопределить все методы в Paint для WindowsButtonUI (специальный пользовательский интерфейс для WindowsLookAndFeel, не работает во всех случаи для WindowsClassicLookAndFeel == лень тестировать), тогда paintChildren будет работать без пинков от пэйнта (следует использовать paintComponent или лучше переопределить paintChildren в JPanel)   -  person mKorbel    schedule 08.03.2014


Ответы (2)


Как потенциальное решение. Вы можете использовать такой класс, как Screen Image, чтобы создать изображение кнопка. Затем просто отобразите изображение и значок на JLabel, и у вас будет реальный компонент для работы, с которым вы можете перетаскивать.

person camickr    schedule 07.03.2014
comment
Я обнаружил проблему благодаря этому классу ScreenImage. Все, что мне нужно было сделать, это вызвать setSize() перед рисованием кнопки. Я бы опубликовал это как ответ с более подробной информацией, но опять же, недостаточно репутации): - person Jouwee; 08.03.2014

Хорошо, я нашел это, благодаря вашему предложению @camickr.

Все, что мне нужно было сделать, это позвонить setSize(int width, int height), прежде чем рисовать. Глядя на исходный код компонента, я нашел это в методе resize (вызывается setSize):

setBoundsOp(ComponentPeer.SET_SIZE);

Я предполагаю, что эта команда сообщает нативному компоненту, что его размер изменился, и без ее вызова фон фактически рисуется, но с размером 0x0.

Так что теперь, вместо того, чтобы создавать класс MyButton и переопределять методы getWidth() и getHeight(), все, что мне нужно сделать, это следующее:

final JButton b = new JButton("Test");
b.setSize(70, 25);

И до сих пор рисую его на моем JPanel.

person Jouwee    schedule 08.03.2014