Когда JPanel рисует (или перекрашивает) свои дочерние компоненты? - PullRequest
4 голосов
/ 02 марта 2011

У меня есть JButton, который нарисован с использованием пользовательского делегата пользовательского интерфейса (CustomButtonUI расширяет BasicButtonUI). Метод paint () в CustomButtonUI рисует кнопку с закругленными «сглаженными» углами, чтобы сделать внешний вид как можно более «плавным».

Каким-то образом "сглаженные" края кнопки исчезают каждый раз, когда я перетаскиваю мышь над кнопка. Это заставляет края кнопки выглядеть «пикселизированными». Однако, как только я добавляю строку кода, чтобы перекрасить родительский элемент кнопки, сглаживание включается, даже когда я перетаскиваю мышь над кнопкой.

Теперь мой вопрос касается того, хорошая ли это идея? Я все-таки перекрашиваю родительский компонент из дочернего компонента. Интересно, приведет ли это к циклу перекрасок? Если родитель пытается перекрасить своих детей, и дети пытаются перекрасить своих родителей - тогда я предполагаю, что мы говорим о цикле.

Я приложил свой код в качестве ссылки. Любые комментарии приветствуются!

public class JCustomButtonUI extends BasicButtonUI {

    @Override
    public void installUI(JComponent c) {
        super.installUI(c);

        AbstractButton b = (AbstractButton) c;
        b.setBorderPainted(false);
    }

    @Override
    public void paint(Graphics g, JComponent c) {

        //Cast the Graphics instance to a Graphics2D instance.
        Graphics2D g2d = (Graphics2D) g;
        JButton b = (JButton) c;

        //Repaint parent component to make sure that we get "antialiased"
        //edges.
        b.getParent().repaint();

        //Get the component's height and width.
        int w = (int) g.getClipBounds().getWidth();
        int h = ((int) g.getClipBounds().getHeight());

        //Make sure the button is drawn with "antialiased" edges.
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(Color.GRAY);
        g2d.fillRoundRect(0, 0, w, h, w, h);       
    }
}

Обновление 1

Чтобы проиллюстрировать псевдоним и сглаженную границу, взгляните на две картинки ниже. Когда я (из метода paintUI ButtonUI) вручную вызывает метод перерисовки родительского JPanel, все границы всегда идеально сглаживаются. Однако, когда я не вызываю вручную метод перерисовки родительского JPanel, границы больше не сглаживаются, когда я наводю указатель мыши на кнопку.

Aliased Antialiased

Обновление 2

Я поделился всем «компонентом», который состоит из JPanel, JSlider и нескольких кнопок J на ​​Snipt. Пожалуйста, получите от http://snipt.org/wnllg.

Обновление 3

Кажется, мне удалось заставить его работать. Вместо того, чтобы рисовать фон JPanel в его методе paintComponent (), я создал JCustomPanelUI, который я установил на JPanel. Я не думаю, что это было само решение, но вместо того, чтобы использовать ширину и высоту из экземпляра Graphics, я попытался использовать widht и height из самой JPanel. Я не совсем уверен, почему все идет не так, когда я использую ширину и высоту из экземпляра Graphics. Я думал, что ширина и высота из экземпляра Graphics уже «подготовлены» в отношении размеров из компонента JPanel. Вы можете посмотреть на последний компонент здесь: http://snipt.org/wnlli,

Ответы [ 5 ]

4 голосов
/ 02 марта 2011

Я сократил пример до простого сглаживания и не могу воспроизвести проблему. Это не зависит от платформы. Я не уверен, почему вы используете getClipBounds().

Добавление:

Фон JPanel (градиент) должен просвечивать сквозь ...

Я обновил пример использования градиентного фона за прозрачной кнопкой; Я поместил сглаженные (слева) и псевдонимы (справа) примеры рядом друг с другом. Я не вижу неожиданного поведения.

ButtonUITest.png

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.plaf.basic.BasicButtonUI;

/** @see http://stackoverflow.com/questions/5169647 */
public class ButtonUITest extends JPanel {

    public ButtonUITest() {
        this.setLayout(new GridLayout(1, 0));
        this.setPreferredSize(new Dimension(640, 480));
        this.add(new CustomButton(true));
        this.add(new CustomButton(false));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        int w = this.getWidth();
        int h = this.getHeight();
        Graphics2D g2d = (Graphics2D) g;
        g2d.setPaint(new GradientPaint(0, 0, Color.blue, w, h, Color.red));
        g2d.fillRect(0, 0, w, h);
    }

    private void display() {
        JFrame f = new JFrame("ButtonUITest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static class CustomButton extends JButton {

        public CustomButton(boolean antialiased) {
            this.setOpaque(false);
            this.setUI(new CustomButtonUI(antialiased));
        }
    }

    private static class CustomButtonUI extends BasicButtonUI {

        private boolean antialiased;

        public CustomButtonUI(boolean antialiased) {
            this.antialiased = antialiased;
        }

        @Override
        public void paint(Graphics g, JComponent c) {
            int w = c.getWidth();
            int h = c.getHeight();
            Graphics2D g2d = (Graphics2D) g;
            if (antialiased) {
                g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            }
            g2d.setColor(Color.LIGHT_GRAY);
            g2d.fillOval(0, 0, w, 2 * h);
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ButtonUITest().display();
            }
        });
    }
}
2 голосов
/ 02 марта 2011

Чтобы сглаживание работало согласованно, ваш компонент должен возвращать false для isOpaque.В противном случае RepaintManager может пропустить рисование области за вашим компонентом.

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

1 голос
/ 02 марта 2011

Эксперты по Swing, которые знают, о чем они говорят, скоро появятся здесь. А пока позвольте мне прокомментировать это:

Теперь мой вопрос относится к Это хорошая идея? Я делаю в конце концов перекрасить родительский компонент из дочерний компонент. Интересно, если это приведет в петлю перекрасок? Если родитель пытается перекрасить своих детей и дети пытаются перекрасить своего родителя - тогда я предполагаю, что мы говорим о цикл.

Как только вы попробуете это и увидите, что это не проблема на вашем компьютере, есть вероятность, что это будет верно для всех JVM, которые вы пробуете. То есть, доказательство в пудинге, или случайная неуклюжесть обычно приводит к положительным результатам в Swing. У рекурсивных циклов есть способ заставить программу довольно быстро останавливаться в Java, поэтому ответ таков: если бы это было совершенно неправильно вы бы уже знали. Плюс вы можете добавить туда sysout, чтобы увидеть, это происходит (это явно не так).

Тем не менее, может быть, есть лучший способ справиться с вашей проблемой, но если ваш ответ работает, придерживайтесь его пока.

0 голосов
/ 02 марта 2011

Выполнение перекраски - это определенно плохая идея. Попробуйте добавить b.setOpaque(false) в installUI(). Ваша кнопка больше не рисует все границы из-за сглаживания. Вы должны позволить фону просвечивать, чтобы заполнить пробелы.

0 голосов
/ 02 марта 2011

AFAIK, repaint () просто говорит системе перекрасить этот компонент в следующем цикле рисования, т. Е. Если вы вызовете repaint () в методе paint (), перерисовка может быть выполнена в следующем цикле.

Как правило, Swing работает в своем собственном потоке, поэтому повторное перерисовывание не должно останавливать логику приложения.Тем не менее, он может использовать вычислительную мощность, если система всегда перерисовывает пользовательский интерфейс, даже если нет никаких изменений.

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

Редактировать: есть класс с именем RepaintManager , который может вас заинтересовать.

...